Esercitazione: Chiamare l'API Microsoft Graph in un'app daemon della console di Node.js

In questa esercitazione si crea un'app daemon console che chiama l'API Microsoft Graph usando la propria identità. L'app daemon compilata usa Microsoft Authentication Library (MSAL) per Node.js.

Seguire la procedura descritta in questa esercitazione per:

  • Registrare l'applicazione nel portale di Azure
  • Creare un progetto di app daemon della console di Node.js
  • Aggiungere la logica di autenticazione all'app
  • Aggiungere i dettagli di registrazione dell'app
  • Aggiungere un metodo per chiamare un'API Web
  • Testare l'app

Prerequisiti

Registrare l'applicazione

Prima di tutto, completare i passaggi descritti in Registrare un'applicazione con Microsoft Identity Platform per registrare l'app.

Usare le impostazioni seguenti per la registrazione dell'app:

  • Nome: NodeDaemonApp (suggerito)
  • Tipi di account supportati: solo account in questa directory organizzativa
  • Autorizzazioni API: Autorizzazioni dell'applicazione Microsoft Graph>per le API Microsoft>>User.Read.All
  • Segreto client: ********* (registrare questo valore da usare in un passaggio successivo: viene visualizzato una sola volta)

Creare il progetto

  1. Per iniziare, creare una directory per questo progetto di esercitazione Node.js. Ad esempio, NodeDaemonApp.

  2. Nel terminale passare alla directory creata (radice del progetto) e quindi eseguire i comandi seguenti:

    npm init -y
    npm install --save dotenv yargs axios @azure/msal-node
    
  3. Modificare quindi il file package.json nella radice del progetto e anteporre il valore di main con bin/, come illustrato di seguito:

    "main": "bin/index.js",
    
  4. Creare ora la directory bin e, all'interno di bin, aggiungere il codice seguente a un nuovo file denominato 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();
    

Il file index.js appena creato fa riferimento a due altri moduli di nodo che verranno creati successivamente:

  • auth.js: usa il nodo MSAL per acquisire i token di accesso da Microsoft Identity Platform.
  • fetch.js: richiede dati dall'API Microsoft Graph includendo i token di accesso (acquisiti in auth.js) nelle richieste HTTP all'API.

Alla fine dell'esercitazione, la struttura di file e directory del progetto dovrebbe essere simile alla seguente:

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

Aggiungere la logica di autenticazione

All'interno della directory bin aggiungere il codice seguente a un nuovo file denominato auth.js. Il codice in auth.js acquisisce un token di accesso da Microsoft Identity Platform per includere nelle richieste dell'API 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://video2.skills-academy.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
};

Nel frammento di codice precedente si crea prima di tutto un oggetto di configurazione (msalConfig) e lo si passa per inizializzare un oggetto MSAL ConfidentialClientApplication. Si crea quindi un metodo per acquisire i token tramite credenziali client e infine si espone questo modulo per l'accesso da parte di main.js. I parametri di configurazione in questo modulo vengono disegnati da un file di ambiente, che verrà creato nel passaggio successivo.

Aggiungere i dettagli di registrazione dell'app

Creare un file di ambiente per archiviare i dettagli di registrazione dell'app che verranno usati durante l'acquisizione dei token. A tale scopo, creare un file denominato .env all'interno della cartella radice dell'esempio (NodeDaemonApp) e aggiungere il codice seguente:

# 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/

Compilare questi dettagli con i valori ottenuti dal portale di registrazione delle app di Azure:

  • Enter_the_Tenant_Id_here deve essere uno dei seguenti:
    • Se l'applicazione supporta account in questa directory organizzativa, sostituire questo valore con l'ID tenant o il nome del tenant. Ad esempio: contoso.microsoft.com.
    • Se l'applicazione supporta account in qualsiasi directory organizzativa, sostituire questo valore con organizations.
    • Se l'applicazione supporta account in qualsiasi directory organizzativa e account Microsoft personali, sostituire questo valore con common.
    • Per limitare il supporto ai soli account Microsoft personali, sostituire questo valore con consumers.
  • Enter_the_Application_Id_Here: ID applicazione (client) dell'applicazione registrata.
  • Enter_the_Cloud_Instance_Id_Here: l'istanza cloud di Azure in cui è registrata l'applicazione.
    • Per il cloud principale (o globale) di Azure, immettere https://login.microsoftonline.com.
    • Peri cloud nazionali, ad esempio Cina, è possibile trovare i valori appropriati nella pagina Cloud nazionali.
  • Enter_the_Graph_Endpoint_Here è l'istanza dell'API Microsoft Graph con cui l'applicazione dovrà comunicare.
    • Per l'endpoint API Microsoft Graph globale, sostituire entrambe le istanze di questa stringa con https://graph.microsoft.com.
    • Per gli endpoint delle distribuzioni di cloud nazionali, vedere Distribuzioni di cloud nazionali nella documentazione di Microsoft Graph.

Aggiungere un metodo per chiamare un'API Web

All'interno della cartella bin creare un altro file denominato fetch.js e aggiungere il codice seguente per effettuare chiamate REST all'API 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
};

In questo caso, il callApi metodo viene usato per effettuare una richiesta HTTP GET su una risorsa protetta che richiede un token di accesso. La richiesta restituisce quindi il contenuto al chiamante. Questo metodo aggiunge il token acquisito nell'intestazione di autorizzazione HTTP. La risorsa protetta qui è l'endpoint degli utenti dell'API Microsoft Graph che visualizza gli utenti nel tenant in cui è registrata l'app.

Testare l'app

La creazione dell'applicazione è stata completata ed è ora possibile testare le funzionalità dell'app.

Avviare l'app daemon della console di Node.js eseguendo il comando seguente dalla radice della cartella del progetto:

node . --op getUsers

Questo dovrebbe comportare una risposta JSON dall'API Microsoft Graph e nella console dovrebbe essere visualizzata una matrice di oggetti utente:

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'
        }
    ]
}

Interfaccia della riga di comando che visualizza la risposta graph

Funzionamento dell'applicazione

Questa applicazione usa la concessione di credenziali client OAuth 2.0. Questo tipo di concessione viene comunemente usato per le interazioni da server a server che devono essere eseguite in background, senza l'interazione immediata con un utente. Il flusso di concessione delle credenziali consente a un servizio Web (client riservato) di usare le proprie credenziali, anziché rappresentare un utente, per eseguire l'autenticazione quando si chiama un altro servizio Web. Il tipo di applicazioni supportate con questo modello di autenticazione è in genere daemon o account di servizio.

L'ambito da richiedere per un flusso di credenziali client è il nome della risorsa seguito da /.default. Questa notazione indica a Microsoft Entra ID di usare le autorizzazioni a livello di applicazione dichiarate in modo statico durante la registrazione dell'applicazione. Inoltre, queste autorizzazioni API devono essere concesse da un amministratore tenant.

Passaggi successivi

Per approfondire Node.js sviluppo di applicazioni daemon in Microsoft Identity Platform, vedere la serie di scenari in più parti: