Tutorial: Chamar uma API Web do aplicativo daemon .Node.js
Este tutorial é a parte final de uma série que demonstra como preparar seu aplicativo cliente daemon Node.js usando o fluxo de concessão de credenciais do cliente OAuth (Open Authorization) 2.0 e configurá-lo para adquirir um token de acesso para chamar uma API Web. Na parte 1 desta série, você registrou uma API Web e um aplicativo daemon no centro de administração do Microsoft Entra e concedeu permissões. Esta etapa final demonstra como compilar um aplicativo Node.js usando a MSAL (biblioteca de autenticação da Microsoft) para Node para simplificar a adição de autorização ao seu aplicativo.
Neste tutorial;
- Crie um aplicativo Node.js no Visual Studio Code e instale dependências.
- Habilite o aplicativo Node.js para adquirir um token de acesso para chamar uma API Web.
Pré-requisitos
- Tutorial: Prepare seu locatário externo para autorizar um aplicativo daemon Node.js.
- Uma API Web protegida que está em execução e pronta para aceitar solicitações. Se você ainda não criou uma, confira o tutorial de criação uma API Web protegida. Verifique se essa API Web está usando os detalhes de registro do aplicativo que você criou no tutorial de preparação do locatário. Verifique se a API Web expõe os seguintes pontos de extremidade por meio de HTTPS:
GET /api/todolist
para obter todos os ToDos.POST /api/todolist
para adicionar um ToDo.
- Node.js.
- Embora qualquer ambiente de desenvolvimento integrado (IDE) compatível com aplicativos do React possa ser utilizado, esse tutorial usa o Visual Studio Code.
- Detalhes de registro para o aplicativo daemon Node.js e a API Web que você criou no tutorial de preparação do locatário.
Criar o projeto daemon do Node.js
Crie uma pasta para hospedar seu aplicativo daemon do Node.js, como ciam-call-api-node-daemon
:
No seu terminal, altere o diretório para a pasta do aplicativo daemon do Node, como
cd ciam-call-api-node-daemon
, e executenpm init -y
. Esse comando cria um arquivo package.json padrão para seu projeto Node.js. Esse comando cria um arquivopackage.json
padrão para seu projeto Node.js.Crie pastas e arquivos adicionais para obter a seguinte estrutura de projeto:
ciam-call-api-node-daemon/ ├── auth.js └── authConfig.js └── fetch.js └── index.js └── package.json
Instalar dependências do aplicativo
No terminal, instale os pacotes axios
, yargs
e @azure/msal-node
com o seguinte comando:
npm install axios yargs @azure/msal-node
Criar objeto de configuração da MSAL
No editor de código, abra o arquivo authConfig.js e adicione o seguinte código:
require('dotenv').config();
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID || 'Enter_the_Application_Id_Here', // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
authority: process.env.AUTHORITY || 'https://Enter_the_Tenant_Subdomain_Here.ciamlogin.com/', // Replace "Enter_the_Tenant_Subdomain_Here" with your tenant subdomain
clientSecret: process.env.CLIENT_SECRET || 'Enter_the_Client_Secret_Here', // Client secret generated from the app
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 'Info',
},
},
};
const protectedResources = {
apiToDoList: {
endpoint: process.env.API_ENDPOINT || 'https://localhost:44351/api/todolist',
scopes: [process.env.SCOPES || 'api://Enter_the_Web_Api_Application_Id_Here'],
},
};
module.exports = {
msalConfig,
protectedResources,
};
O objeto msalConfig
contém um conjunto de opções de configuração que você usará para personalizar o comportamento do fluxo de autorização.
No arquivo authConfig.js, substitua:
Enter_the_Application_Id_Here
pela ID do aplicativo (cliente) do aplicativo daemon cliente que você registrou anteriormente.Enter_the_Tenant_Subdomain_Here
e substitua-o pelo subdomínio diretório (locatário). Por exemplo, se o domínio primário do locatário forcontoso.onmicrosoft.com
, usecontoso
. Se você não tiver o nome do locatário, saiba como ler os detalhes do locatário.Enter_the_Client_Secret_Here
pelo valor do segredo do aplicativo daemon do cliente que você copiou anteriormente.Enter_the_Web_Api_Application_Id_Here
pela ID do aplicativo (cliente) do aplicativo de API Web que você copiou anteriormente.
Observe que a propriedade scopes
na variável protectedResources
é o identificador de recurso (URI da ID do aplicativo) da API Web que você registrou anteriormente. O URI de escopo completo é semelhante a api://Enter_the_Web_Api_Application_Id_Here/.default
.
Adquirir um token de acesso
No editor de código, abra o arquivo auth.js e adicione o seguinte código:
const msal = require('@azure/msal-node');
const { msalConfig, protectedResources } = require('./authConfig');
/**
* With client credentials flows permissions need to be granted in the portal by a tenant administrator.
* The scope is always in the format '<resource-appId-uri>/.default'. For more, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
*/
const tokenRequest = {
scopes: [`${protectedResources.apiToDoList.scopes}/.default`],
};
const apiConfig = {
uri: protectedResources.apiToDoList.endpoint,
};
/**
* Initialize a confidential client application. For more info, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/initialize-confidential-client-application.md
*/
const cca = new msal.ConfidentialClientApplication(msalConfig);
/**
* Acquires token with client credentials.
* @param {object} tokenRequest
*/
async function getToken(tokenRequest) {
return await cca.acquireTokenByClientCredential(tokenRequest);
}
module.exports = {
apiConfig: apiConfig,
tokenRequest: tokenRequest,
getToken: getToken,
};
No código:
Prepare os objetos
tokenRequest
eapiConfig
. OtokenRequest
contém o escopo para o qual você solicitará um token de acesso. O escopo se parece comapi://Enter_the_Web_Api_Application_Id_Here/.default
. O objetoapiConfig
contém o ponto de extremidade para sua API Web. Saiba mais sobre o fluxo de credenciais do cliente OAuth 2.0.Você criará uma instância de cliente confidencial passando o objeto
msalConfig
para o construtor da classe ConfidentialClientApplication.const cca = new msal.ConfidentialClientApplication(msalConfig);
Em seguida, usará a função acquireTokenByClientCredential para adquirir um token de acesso. Essa lógica será implementada na função
getToken
:cca.acquireTokenByClientCredential(tokenRequest);
Depois de adquirir um token de acesso, você pode prosseguir chamando uma API.
Chamar uma API
No editor de código, abra o arquivo fetch.js e adicione o seguinte código:
const axios = require('axios');
/**
* Calls the endpoint with authorization bearer token.
* @param {string} endpoint
* @param {string} accessToken
*/
async function callApi(endpoint, accessToken) {
const options = {
headers: {
Authorization: `Bearer ${accessToken}`
}
};
console.log('request made to web API at: ' + new Date().toString());
try {
const response = await axios.get(endpoint, options);
return response.data;
} catch (error) {
console.log(error)
return error;
}
};
module.exports = {
callApi: callApi
};
Nesse código, você faz uma chamada para a API Web, passando o token de acesso como um token de portador no cabeçalho Authorization
da solicitação:
Authorization: `Bearer ${accessToken}`
Você usará o token de acesso adquirido anteriormente em Adquirir um token de acesso.
Depois que a API Web receber a solicitação, ela a avaliará e irá determinar que é uma solicitação de aplicativo. Se o token de acesso for válido, a API Web retornará os dados solicitados. Caso contrário, a API retornará um erro HTTP 401 Unauthorized
.
Finalizar seu aplicativo daemon
No editor de código, abra o arquivo index.js e adicione o seguinte código:
#!/usr/bin/env node
// read in env settings
require('dotenv').config();
const yargs = require('yargs');
const fetch = require('./fetch');
const auth = require('./auth');
const options = yargs
.usage('Usage: --op <operation_name>')
.option('op', { alias: 'operation', describe: 'operation name', type: 'string', demandOption: true })
.argv;
async function main() {
console.log(`You have selected: ${options.op}`);
switch (yargs.argv['op']) {
case 'getToDos':
try {
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
} catch (error) {
console.log(error);
}
break;
default:
console.log('Select an operation first');
break;
}
};
main();
Esse código é o ponto de entrada para seu aplicativo. Você utilizará a biblioteca de análise de argumentos de linha de comando yargs JavaScript para aplicativos Node.js, a fim de obter interativamente um token de acesso e, em seguida, chamar uma API. Você usará as funções getToken
e callApi
que definiu anteriormente:
const authResponse = await auth.getToken(auth.tokenRequest);
const todos = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
Executar e testar o aplicativo daemon e a API
Neste ponto, você está pronto para testar seu aplicativo daemon cliente e a API Web:
Use as etapas aprendidas no tutorial Proteger uma ASP.NET Web API para iniciar sua API Web. Sua API Web agora está pronta para atender às solicitações do cliente. Se você não executar sua API Web na porta
44351
conforme especificado no arquivo authConfig.js, atualize o arquivo authConfig.js para usar o número correto da porta da API Web.Em seu terminal, verifique se você está na pasta do projeto que contém o aplicativo daemon Node.js, como
ciam-call-api-node-daemon
, e execute o seguinte comando:node . --op getToDos
Se o aplicativo daemon e a API Web forem executados com sucesso, você deverá encontrar os dados retornados pela variável todos
do ponto de extremidade da API Web, semelhantes à matriz JSON a seguir, na janela do seu console:
{
id: 1,
owner: '3e8....-db63-43a2-a767-5d7db...',
description: 'Pick up grocery'
},
{
id: 2,
owner: 'c3cc....-c4ec-4531-a197-cb919ed.....',
description: 'Finish invoice report'
},
{
id: 3,
owner: 'a35e....-3b8a-4632-8c4f-ffb840d.....',
description: 'Water plants'
}