Tutorial: chamar a API do Microsoft Graph em um aplicativo daemon de console Node.js

Neste tutorial, você criará um aplicativo daemon de console que chama a API do Microsoft Graph usando a própria identidade. O aplicativo daemon que você criará usa a MSAL (Biblioteca de Autenticação da Microsoft) para Node.js.

Siga as etapas deste tutorial para:

  • Registrar o aplicativo no portal do Azure
  • Criar um projeto de aplicativo daemon de console Node.js
  • Adicionar a lógica de autenticação ao aplicativo
  • Adicionar detalhes de registro do aplicativo
  • Adicionar um método para chamar uma API Web
  • Testar o aplicativo

Pré-requisitos

Registrar o aplicativo

Primeiro, conclua as etapas descritas em Registrar um aplicativo com a plataforma de identidade da Microsoft para registrar seu aplicativo.

Use as seguintes configurações para o registro do aplicativo:

  • Nome: NodeDaemonApp (sugerido)
  • Tipos de conta compatíveis: Contas somente neste diretório organizacional
  • Permissões de API: APIs da Microsoft>Microsoft Graph>Permissões de Aplicativo>User.Read.All
  • Segredo do cliente: ********* (registre esse valor para uso em uma etapa posterior – ele é mostrado apenas uma vez)

Criar o projeto

  1. Comece a criar um diretório para este projeto do tutorial Node.js. Por exemplo, NodeDaemonApp.

  2. Em seu terminal, altere para o diretório que você criou (a raiz do projeto) e, em seguida, execute os seguintes comandos:

    npm init -y
    npm install --save dotenv yargs axios @azure/msal-node
    
  3. Em seguida, edite o arquivo package.json na raiz do projeto e prefixe o valor de main com bin/, desta forma:

    "main": "bin/index.js",
    
  4. Agora, crie o diretório bin e, dentro do bin, adicione o seguinte código a um novo arquivo chamado index.js:

    #!/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 'getUsers':
    
                try {
                    // here we get an access token
                    const authResponse = await auth.getToken(auth.tokenRequest);
    
                    // call the web API with the access token
                    const users = await fetch.callApi(auth.apiConfig.uri, authResponse.accessToken);
    
                    // display result
                    console.log(users);
                } catch (error) {
                    console.log(error);
                }
    
                break;
            default:
                console.log('Select a Graph operation first');
                break;
        }
    };
    
    main();
    

O arquivo de index.js que você acabou de criar faz referência a dois outros módulos de nó que você criará a seguir:

  • auth.js – usa o nó MSAL para adquirir tokens de acesso da plataforma de identidade da Microsoft.
  • fetch.js – solicita dados da API do Microsoft Graph incluindo tokens de acesso (adquiridos em auth.js) nas solicitações HTTP para a API.

No final do tutorial, a estrutura de arquivos e diretórios do seu projeto deve ser semelhante a esta:

NodeDaemonApp/
├── bin
│   ├── auth.js
│   ├── fetch.js
│   ├── index.js
├── package.json
└── .env

Adicionar a lógica de autenticação

Dentro do diretório bin, adicione o seguinte código a um novo arquivo chamado auth.js. O código em auth.js adquire um token de acesso da plataforma de identidade da Microsoft para incluir nas solicitações da API do Microsoft Graph.

const msal = require('@azure/msal-node');

/**
 * 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,
        authority: process.env.AAD_ENDPOINT + '/' + process.env.TENANT_ID,
        clientSecret: process.env.CLIENT_SECRET,
    }
};

/**
 * With client credentials flows permissions need to be granted in the portal by a tenant administrator.
 * The scope is always in the format '<resource>/.default'. For more, visit:
 * https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
 */
const tokenRequest = {
    scopes: [process.env.GRAPH_ENDPOINT + '/.default'],
};

const apiConfig = {
    uri: process.env.GRAPH_ENDPOINT + '/v1.0/users',
};

/**
 * 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 snippet de código acima, primeiro criamos um objeto de configuração (msalConfig) e o transmitimos para inicializar um ConfidentialClientApplication da MSAL. Em seguida, criamos um método para adquirir tokens por meio das credenciais do cliente e, finalmente, expomos esse módulo para ser acessado pelo main.js. Os parâmetros de configuração deste módulo foram extraídos de um arquivo de ambiente, que criaremos na próxima etapa.

Adicionar detalhes de registro do aplicativo

Crie um arquivo de ambiente para armazenar os detalhes de registro do aplicativo que serão usados ao adquirir tokens. Para fazer isso, crie um arquivo chamado .env dentro da pasta raiz do exemplo (NodeDaemonApp) e adicione o seguinte código:

# Credentials
TENANT_ID=Enter_the_Tenant_Id_Here
CLIENT_ID=Enter_the_Application_Id_Here
CLIENT_SECRET=Enter_the_Client_Secret_Here

# Endpoints
AAD_ENDPOINT=Enter_the_Cloud_Instance_Id_Here/
GRAPH_ENDPOINT=Enter_the_Graph_Endpoint_Here/

Preencha esses detalhes com os valores obtidos do portal de registro de aplicativo do Azure:

  • Enter_the_Tenant_Id_here deve ser uma destas opções:
    • Se o aplicativo tem suporte para contas neste diretório organizacional, substitua esse valor pela ID do locatário ou pelo Nome do locatário. Por exemplo, contoso.microsoft.com.
    • Se o aplicativo tem suporte para contas em qualquer diretório organizacional, substitua esse valor por organizations.
    • Se o seu aplicativo tem suporte para contas em qualquer diretório organizacional e contas pessoais Microsoft, substitua esse valor por common.
    • Para restringir o suporte a contas pessoais da Microsoft, substitua esse valor por consumers.
  • Enter_the_Application_Id_Here: a ID do Aplicativo (cliente) do aplicativo que você registrou.
  • Enter_the_Cloud_Instance_Id_Here: a instância de nuvem do Azure na qual o aplicativo está registrado.
    • Para a nuvem principal (ou global) do Azure, insira https://login.microsoftonline.com.
    • Para nuvens nacionais (por exemplo, China), você pode encontrar os valores apropriados em Nuvens nacionais.
  • Enter_the_Graph_Endpoint_Here é a instância da API do Microsoft Graph com a qual o aplicativo deve se comunicar.
    • Para o ponto de extremidade global da API do Microsoft Graph, substitua as duas instâncias dessa cadeia de caracteres por https://graph.microsoft.com.
    • Para pontos de extremidade em implantações de nuvens nacionais, confira Implantações de nuvens nacionais na documentação do Microsoft Graph.

Adicionar um método para chamar uma API Web

Na pasta bin, crie outro arquivo chamado fetch.js e adicione o seguinte código para fazer chamadas REST à API do Microsoft Graph:

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
};

Aqui, o método callApi é usado para fazer uma solicitação HTTP GET em um recurso protegido que exige um token de acesso. Depois, a solicitação retorna o conteúdo para o chamador. Esse método adiciona o token adquirido no cabeçalho de Autorização HTTP. O recurso protegido aqui é o ponto de extremidade de usuários da API do Microsoft Graph que exibe os usuários no locatário em que esse aplicativo está registrado.

Testar o aplicativo

Você concluiu a criação do aplicativo e agora está pronto para testar a funcionalidade do aplicativo.

Inicie o aplicativo daemon de console Node.js executando o seguinte comando na raiz da pasta do seu projeto:

node . --op getUsers

Isso resultará em uma resposta JSON da API do Microsoft Graph, e você verá uma matriz de objetos de usuário no console:

You have selected: getUsers
request made to web API at: Fri Jan 22 2021 09:31:52 GMT-0800 (Pacific Standard Time)
{
    '@odata.context': 'https://graph.microsoft.com/v1.0/$metadata#users',
    value: [
        {
            displayName: 'Adele Vance'
            givenName: 'Adele',
            jobTitle: 'Retail Manager',
            mail: 'AdeleV@msaltestingjs.onmicrosoft.com',
            mobilePhone: null,
            officeLocation: '18/2111',
            preferredLanguage: 'en-US',
            surname: 'Vance',
            userPrincipalName: 'AdeleV@msaltestingjs.onmicrosoft.com',
            id: '00aa00aa-bb11-cc22-dd33-44ee44ee44ee'
        }
    ]
}

Command-line interface displaying Graph response

Como o aplicativo funciona

Este aplicativo usa a concessão das credenciais do cliente OAuth 2.0. Esse tipo de concessão normalmente é usado para interações de servidor para servidor que devem ser executadas em segundo plano, sem interação imediata com um usuário. O fluxo de concessão de credenciais permite que um serviço Web (um cliente confidencial) use credenciais próprias, em vez de representar um usuário, para autenticação ao chamar outro serviço Web. Os tipos de aplicativos compatíveis com esse modelo de autenticação geralmente são daemons ou contas de serviço.

O escopo a ser solicitado para um fluxo de credenciais do cliente é o nome do recurso seguido por /.default. Essa notação instrui a ID do Microsoft Entra a usar as permissões de nível do aplicativo declaradas estaticamente durante o registro de aplicativo. Além disso, essas permissões de API precisam ser concedidas por um administrador de locatários.

Próximas etapas

Caso deseje se aprofundar no desenvolvimento de aplicativos daemon de console Node.js na plataforma de identidade da Microsoft, confira nossa série de cenários de várias partes: