Proteggere l'API Web Node.js con Azure Active DirectorySecure Node.js Web API with Azure Active Directory

Questo articolo descrive come proteggere un endpoint dell'API Restify con Passport usando il modulo passport-azure-ad per gestire le comunicazioni con Azure Active Directory (AAD).This article demonstrates how to secure a Restify API endpoint with Passport using the passport-azure-ad module to handle communication with Azure Active Directory (AAD).

Questa esercitazione è incentrata sui vari aspetti correlati alla protezione degli endpoint dell'API.The scope of this tutorial covers the concerns regarding securing API endpoints. Gli aspetti che riguardano l'accesso e il mantenimento dei token di autenticazione non sono implementati negli esempi qui riportati e sono di responsabilità di un'applicazione client.The concerns of signing in and retaining authentication tokens are not implemented here and are the responsibility of a client application. Per informazioni dettagliate sugli aspetti correlati a un'implementazione client, vedere Accesso e disconnessione all'app Web di Node.js con Azure AD.For details surrounding a client implementation, review Node.js web app sign-in and sign-out with Azure AD.

L'esempio di codice completo associato all'articolo è disponibile in GitHub.The full code sample associated with this article is available on GitHub.

Creare il progetto di esempioCreate the sample project

Per questa applicazione server sono necessarie alcune dipendenze di pacchetto per supportare Restify e Passport, oltre a informazioni per l'account che vengono passate ad AAD.This server application requires a few package dependencies to support Restify and Passport as well as account information that is passed to AAD.

Per iniziare, aggiungere il codice seguente in un file denominato package.json:To begin, add the following code into a file named package.json:

{
  "name": "node-aad-demo",
  "version": "0.0.1",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "passport": "0.4.0",
    "passport-azure-ad": "3.0.8",
    "restify": "6.0.1",
    "restify-plugins": "1.6.0"
  }
}

Dopo aver creato package.json, eseguire npm install nel prompt dei comandi per installare le dipendenze di pacchetto.Once package.json is created, run npm install in your command prompt to install the package dependencies.

Configurare il progetto per l'uso di Active DirectoryConfigure the project to use Active Directory

Per iniziare la configurazione dell'applicazione, esistono alcuni valori specifici dell'account che è possibile ottenere tramite l'interfaccia della riga di comando di Azure.To get started configuring the application, there are a few account-specific values you can obtain from the Azure CLI. Il modo più semplice per iniziare ad acquisire familiarità con l'interfaccia della riga di comando consiste nell'usare Azure Cloud Shell.The easiest way to get started with the CLI is to use the Azure Cloud Shell.

Avviare Azure Cloud ShellLaunch Azure Cloud Shell

Azure Cloud Shell è una shell Bash gratuita che può essere eseguita direttamente nel portale di Azure.The Azure Cloud Shell is a free Bash shell that you can run directly within the Azure portal. Include l'interfaccia della riga di comando di Azure preinstallata e configurata per l'uso con l'account.It has the Azure CLI preinstalled and configured to use with your account. Fare clic sul pulsante Cloud Shell nel menu nel riquadro in alto a destra nel portale di Azure.Click the Cloud Shell button on the menu in the upper-right of the Azure portal.

Cloud ShellCloud Shell

Il pulsante avvia una shell interattiva che è possibile usare per eseguire i passaggi di questo argomento:The button launches an interactive shell that you can use to run the steps in this topic:

Screenshot che mostra la finestra di Cloud Shell nel portaleScreenshot showing the Cloud Shell window in the portal

Immettere il comando seguente in Cloud Shell:Enter the following command in the cloud shell:

az ad app create --display-name node-aad-demo --homepage http://localhost --identifier-uris http://node-aad-demo

Gli argomenti per il comando create includono:The arguments for the create command include:

ArgomentoArgument DescrizioneDescription
display-name Nome descrittivo della registrazioneFriendly name of the registration
homepage URL a cui possono accedere gli utenti per usare l'applicazioneUrl where users can sign in and use your application
identifier-uris URI univoci delimitati da spazio che possono essere usati da Azure AD per questa applicazioneSpace separated unique URIs that Azure AD can use for this application

Prima di connettersi ad Azure Active Directory, sono necessarie le informazioni seguenti:Before you can connect to Azure Active Directory, you need the following information:

NomeName DescrizioneDescription Nome della variabile nel file di configurazioneVariable Name in Config File
Nome del tenantTenant Name Nome del tenant che si vuole usare per l'autenticazioneTenant name you want to use for authentication tenantName
ID clientClient ID ID client è il termine di OAuth usato per l'ID applicazione di AAD.Client ID is the OAuth term used for the AAD Application ID. clientID

Copiare il valore appId dalla risposta di registrazione in Azure Cloud Shell e creare un nuovo file denominato config.js.From the registration response in the Azure Cloud Shell, copy the appId value and create a new file named config.js. Aggiungere quindi il codice seguente e sostituire i valori con i token tra parentesi:Next, add in the following code and replace your values with the bracketed tokens:

const tenantName    = //<YOUR_TENANT_NAME>;
const clientID      = //<YOUR_APP_ID_FROM_CLOUD_SHELL>;
const serverPort    = 3000;

module.exports.serverPort = serverPort;

module.exports.credentials = {
  identityMetadata: `https://login.microsoftonline.com/${tenantName}.onmicrosoft.com/.well-known/openid-configuration`, 
  clientID: clientID
};

Per altre informazioni sulle singole impostazioni di configurazione, vedere la documentazione per il modulo passport-azure-ad.For more information regarding the individual configuration settings, review the passport-azure-ad module documentation.

Implementare il serverImplement the server

Il modulo passport-azure-ad include due strategie di autenticazione: OIDC e Bearer.The passport-azure-ad module features two authentication strategies: OIDC and Bearer strategies. Il server implementato in questo articolo usa la strategia Bearer per proteggere l'endpoint dell'API.The server implemented in this article uses the Bearer strategy to secure the API endpoint.

Passaggio 1: Importare le dipendenzeStep 1: Import dependencies

Creare un nuovo file denominato app.js e incollare il testo seguente:Create a new file named app.js and paste in the following text:

const
      restify = require('restify')
    , restifyPlugins = require('restify-plugins')
    , passport = require('passport')
    , BearerStrategy = require('passport-azure-ad').BearerStrategy
    , config = require('./config')
    , authenticatedUserTokens = []
    , serverPort = process.env.PORT || config.serverPort
;

In questa sezione del codice:In this section of code:

  • Si fa riferimento in ordine ai moduli restify e restify-plugins per configurare un server Restify.The restify and restify-plugins modules are referenced in order to set up a Restify server.

  • I moduli passport e passport-azure-ad sono responsabili per la comunicazione con AAD.The passport and passport-azure-ad modules are responsible for communicating with AAD.

  • La variabile config viene inizializzata con i valori dal file config.js creato nel passaggio precedente.The config variable is initialized with values from the config.js file created in the previous step.

  • Viene creata una matrice per authenticatedUserTokens per archiviare i token dell'utente passati negli endpoint protetti.An array is created for authenticatedUserTokens to store user tokens as they are passed into secured endpoints.

  • Il valore serverPort viene definito in base alla porta dell'ambiente di elaborazione o dal file di configurazione.The serverPort is either defined from the process environment's port or from the configuration file.

Passaggio 2: Creare un'istanza di una strategia di autenticazioneStep 2: Instantiate an authentication strategy

Per la protezione di un endpoint è necessario specificare una strategia responsabile di determinare se la richiesta corrente proviene o meno da un utente autenticato.As you secure an endpoint, you must provide a strategy responsible for determining whether or not the current request originates from an authenticated user. In questo caso la variabile authenticatonStrategy è un'istanza della classe BearerStrategy di passport-azure-ad.Here the authenticatonStrategy variable is an instance of the passport-azure-ad BearerStrategy class. Aggiungere il codice seguente dopo le istruzioni require.Add the following code after the require statements.

const authenticationStrategy = new BearerStrategy(config.credentials, (token, done) => {
    let userToken = authenticatedUserTokens.find((user) => user.sub === token.sub);

    if(!userToken) {
        authenticatedUserTokens.push(token);
    }

    return done(null, user, token);
});

Questa implementazione usa la registrazione automatica aggiungendo i token di autenticazione nella matrice authenticatedUserTokens se non esistono già.This implementation uses auto-registration by adding authentication tokens into the authenticatedUserTokens array if they do not already exist.

Dopo aver creato una nuova istanza della strategia, è necessario passarla a Passport tramite il metodo use.Once a new instance of the strategy is created, you must pass it into Passport via the use method. Aggiungere il seguente codice in app.js per usare la strategia in Passport.Add the following code to app.js to use the strategy in Passport.

passport.use(authenticationStrategy);

Passaggio 3: Configurazione del serverStep 3: Server configuration

Dopo aver definito la strategia di autenticazione, è ora possibile configurare il server Restify con alcune impostazioni di base e impostarlo per l'uso di Passport per la sicurezza.With the authentication strategy defined, you can now set up the Restify server with some basic settings and set to use Passport for security.

const server = restify.createServer({ name: 'Azure Active Directroy with Node.js Demo' });
server.use(restifyPlugins.authorizationParser());
server.use(passport.initialize());
server.use(passport.session());

Questo server viene inizializzato e configurato in modo da analizzare le intestazioni di autorizzazione e quindi impostato per l'uso di Passport.This server is initialized and configured to parse authorization headers and then set to use Passport.

Passaggio 4: Definire le routeStep 4: Define routes

È ora possibile definire le route e decidere quali proteggere con AAD.You can now define routes and decide which to secure with AAD. Questo progetto include due route con livello radice aperto e la route /api è impostata per richiedere l'autenticazione.This project includes two routes where the root level is open and the /api route is set to require authentication.

In app.js aggiungere il codice seguente alla route del livello radice:In app.js add the following code for the root level route:

server.get('/', (req, res, next) => {
    res.send(200, 'Try: curl -isS -X GET http://127.0.0.1:3000/api');
    next();
});

La route radice consente tutte le richieste attraverso la route e restituisce un messaggio che include un comando per testare la route /api.The root route allows all requests through the route and returns a message that includes a command to test the /api route. Al contrario, la route /api è bloccata tramite passport.authenticate.By contrast, the /api route is locked down using passport.authenticate. Aggiungere il codice seguente dopo la route radice.Add the following code after the root route.

server.get('/api', passport.authenticate('oauth-bearer', { session: false }), (req, res, next) => {
    res.json({ message: 'response from API endpoint' });
    return next();
});

Questa configurazione consente solo le richieste autenticate che includono l'accesso con token di connessione a /api.This configuration only allows authenticated requests that include a bearer token access to /api. L'opzione session: false viene usata per disabilitare le sessioni per richiedere il passaggio di un token con ogni richiesta all'API.The option of session: false is used to disable sessions to require that a token is passed with each request to the API.

Il server viene infine impostato per l'ascolto sulla porta configurata tramite la chiamata del metodo listen.Finally, the server is set to listen on the configured port by calling the listen method.

server.listen(serverPort);

Eseguire l'esempioRun the sample

Ora che il server è implementato, è possibile avviare il server. Aprire un prompt dei comandi e immettere:Now that the server is implemented, you can start the server by opening up a command prompt and enter:

npm start

Con il server in esecuzione, è possibile inviare una richiesta al server per testare i risultati.With the server running, you can submit a request to the server to test the results. Per una dimostrazione di risposta dalla route radice, aprire una shell Bash e immettere il codice seguente:To demonstrate the response from the root route, open a bash shell and enter the following code:

curl -isS -X GET http://127.0.0.1:3000/

Se il server è stato configurato correttamente, la risposta sarà simile alla seguente:If you have configured your server correctly, the response should look similar to:

HTTP/1.1 200 OK
Server: Azure Active Directroy with Node.js Demo
Content-Type: application/json
Content-Length: 49
Date: Tue, 10 Oct 2017 18:35:13 GMT
Connection: keep-alive

Try: curl -isS -X GET http://127.0.0.1:3000/api

A questo punto è possibile testare la route che richiede l'autenticazione immettendo il comando seguente nella shell Bash:Next, you can test the route that requires authentication by entering the following command into your bash shell:

curl -isS -X GET http://127.0.0.1:3000/api

Se il server è stato configurato correttamente, dovrebbe rispondere con lo stato Unauthorized.If you have configured the server correctly, then the server should respond with a status of Unauthorized.

HTTP/1.1 401 Unauthorized
Server: Azure Active Directroy with Node.js Demo
WWW-Authenticate: token is not found
Date: Tue, 10 Oct 2017 16:22:03 GMT
Connection: keep-alive
Content-Length: 12

Unauthorized

Dopo avere creato un'API protetta, è possibile implementare un client in grado di passare i token di autenticazione all'API.Now that you have created a secure API, you can implement a client that is able to pass authentication tokens to the API.

Passaggi successiviNext steps

Come indicato nell'introduzione, è necessario implementare una controparte client per la connessione al server che gestisce l'accesso, la disconnessione e la gestione dei token.As stated in the introduction, you must implement a client counterpart to connect to the server that handles signing in, signing out and managing tokens. Per esempi basati su codice può fare riferimento alle applicazioni client in iOS e Android.For code-based examples, you may refer to the client applications in iOS and Android. Per un'esercitazione dettagliata vedere l'articolo seguente:For a step-by-step tutorial refer to the following article: