Proteggere un'API Web usando node.jsSecure a web API by using Node.js

Nota

Non tutti gli scenari e le funzionalità di Azure Active Directory usano l'endpoint v2.0.Not all Azure Active Directory scenarios and features work with the v2.0 endpoint. Per determinare se è necessario usare l'endpoint v2.0 o l'endpoint v1.0, leggere le informazioni sulle limitazioni della versione 2.0.To determine whether you should use the v2.0 endpoint or the v1.0 endpoint, read about v2.0 limitations.

Quando si usa l'endpoint di Azure Active Directory (Azure AD) v 2.0, è possibile usare i token di accesso OAuth 2.0 per proteggere l'API Web.When you use the Azure Active Directory (Azure AD) v2.0 endpoint, you can use OAuth 2.0 access tokens to protect your web API. Con i token di accesso OAuth 2.0, gli utenti che dispongono sia di un account Microsoft personale che di un account aziendale o dell'istituto di istruzione possono accedere in modo sicuro all'API Web.With OAuth 2.0 access tokens, users who have both a personal Microsoft account and work or school accounts can securely access your web API.

Passport è il middleware di autenticazione per Node.js.Passport is authentication middleware for Node.js. Passport, flessibile e modulare, può essere rilasciato in modo non invadente in qualsiasi applicazione Web basata su Express o Restify.Flexible and modular, Passport can be unobtrusively dropped into any Express-based or restify web application. In Passport una gamma completa di strategie supporta l'autenticazione usando un nome utente e password, Facebook, Twitter o altre opzioni.In Passport, a comprehensive set of strategies support authentication by using a username and password, Facebook, Twitter, or other options. È stata sviluppata una strategia per Azure AD.We have developed a strategy for Azure AD. Questo articolo illustra come installare il modulo e quindi aggiungere il plug-in passport-azure-ad di Azure AD.In this article, we show you how to install the module, and then add the Azure AD passport-azure-ad plug-in.

ScaricareDownload

Il codice per questa esercitazione è salvato su GitHub.The code for this tutorial is maintained on GitHub. Per seguire l'esercitazione, è possibile scaricare la struttura dell'app come file con estensione zip o clonare la struttura:To follow the tutorial, you can download the app's skeleton as a .zip file, or clone the skeleton:

git clone --branch skeleton https://github.com/AzureADQuickStarts/AppModelv2-WebAPI-nodejs.git

Al termine dell'esercitazione, è anche possibile ottenere l'applicazione completata.You also can get the completed application at the end of this tutorial.

1: Registrare un'app1: Register an app

Per registrare un'app creare una nuova app in apps.dev.microsoft.com o seguire questa procedura dettagliata.Create a new app at apps.dev.microsoft.com, or follow these detailed steps to register an app. Assicurarsi di:Make sure you:

  • Copiare l'ID applicazione assegnato all'app.Copy the Application Id assigned to your app. Per questa esercitazione occorre:You'll need it for this tutorial.
  • Aggiungere la piattaforma Mobile per l'app.Add the Mobile platform for your app.
  • Copiare l' URI di reindirizzamento dal portale.Copy the Redirect URI from the portal. È necessario usare il valore URI predefinito urn:ietf:wg:oauth:2.0:oob.You must use the default URI value of urn:ietf:wg:oauth:2.0:oob.

2: Installare Node.js2: Install Node.js

Per usare l'esempio di questa esercitazione, è necessario installare Node.js.To use the sample for this tutorial, you must install Node.js.

3: Installare MongoDB3: Install MongoDB

Per usare correttamente questo esempio, è necessario installare MongoDB.To successfully use this sample, you must install MongoDB. Questo esempio usa MongoDB per rendere l'API REST persistente nelle istanze del server.In this sample, you use MongoDB to make your REST API persistent across server instances.

Nota

In questo articolo si presuppone che si usino gli endpoint di server e di installazione predefiniti per MongoDB: mongodb://localhost.In this article, we assume that you use the default installation and server endpoints for MongoDB: mongodb://localhost.

4: Installare i moduli Restify nell'API Web4: Install the restify modules in your web API

Si usa Resitfy per compilare l'API REST.We use Resitfy to build our REST API. Restify è un framework applicazioni di Node.js minimo e flessibile derivato da Express,Restify is a minimal and flexible Node.js application framework that's derived from Express. Restify è un set affidabile di funzionalità che è possibile usare per compilare API REST su Connect.Restify has a robust set of features that you can use to build REST APIs on top of Connect.

Installare RestifyInstall restify

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

    Se la directory azuread non esiste, crearla:If the azuread directory does not exist, create it:

    mkdir azuread

  2. Installare Restify:Install restify:

    npm install restify

    L'output di questo comando dovrebbe essere simile al seguente:The output of this command should look like this:

    restify@2.6.1 node_modules/restify
    ├── assert-plus@0.1.4
    ├── once@1.3.0
    ├── deep-equal@0.0.0
    ├── escape-regexp-component@1.0.2
    ├── qs@0.6.5
    ├── tunnel-agent@0.3.0
    ├── keep-alive-agent@0.0.1
    ├── lru-cache@2.3.1
    ├── node-uuid@1.4.0
    ├── negotiator@0.3.0
    ├── mime@1.2.11
    ├── semver@2.2.1
    ├── spdy@1.14.12
    ├── backoff@2.3.0
    ├── formidable@1.0.14
    ├── verror@1.3.6 (extsprintf@1.0.2)
    ├── csv@0.3.6
    ├── http-signature@0.10.0 (assert-plus@0.1.2, asn1@0.1.11, ctype@0.5.2)
    └── bunyan@0.22.0(mv@0.0.5)
    

È stato visualizzato un errore?Did you get an error?

In alcuni sistemi operativi, quando si usa il comando npm, è possibile che venga visualizzato questo messaggio: Error: EPERM, chmod '/usr/local/bin/..'.On some operating systems, when you use the npm command, you might see this message: Error: EPERM, chmod '/usr/local/bin/..'. Il messaggio di errore è seguito da una richiesta in cui è indicato di provare a eseguire l'account come amministratore.The error is followed by a request that you try running the account as an administrator. In tal caso, usare il comando sudo per eseguire npm a un livello di privilegi più elevato.If this occurs, use the command sudo to run npm at a higher privilege level.

Quando si installa Restify, è possibile che venga visualizzato questo messaggio:When you install restify, you might see this message:

clang: error: no such file or directory: 'HD/azuread/node_modules/restify/node_modules/dtrace-provider/libusdt'
make: *** [Release/DTraceProviderBindings.node] Error 1
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: two
gyp ERR! stack     at ChildProcess.onExit (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:267:23)
gyp ERR! stack     at ChildProcess.EventEmitter.emit (events.js:98:17)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (child_process.js:789:12)
gyp ERR! System Darwin 13.1.0
gyp ERR! command "node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /Volumes/Development HD/azuread/node_modules/restify/node_modules/dtrace-provider
gyp ERR! node -v v0.10.11
gyp ERR! node-gyp -v v0.10.0
gyp ERR! not ok
npm WARN optional dep failed, continuing dtrace-provider@0.2.8

Restify dispone di un meccanismo efficace per tenere traccia delle chiamate REST usando DTrace.Restify has a powerful mechanism to trace REST calls by using DTrace. Tuttavia, in molti sistemi operativi DTrace non è disponibile.However, DTrace is not available on many operating systems. È possibile ignorare questo messaggio di errore.You can safely ignore this error message.

5: Installare Passport.js nell'API Web5: Install Passport.js in your web API

  1. Al prompt dei comandi passare alla directory azuread.At the command prompt, change the directory to azuread.

  2. Installare Passport.js:Install Passport.js:

    npm install passport

    L'output di questo comando dovrebbe essere simile al seguente:The output of the command should look like this:

     passport@0.1.17 node_modules\passport
    ├── pause@0.0.1
    └── pkginfo@0.2.3
    

6: Aggiungere passport-azure-ad all'API Web6: Add passport-azure-ad to your web API

Quindi, aggiungere la strategia di OAuth, usando passport azuread.Next, add the OAuth strategy, by using passport-azuread. passport-azuread è una suite di strategie che connettono Azure AD a Passport.passport-azuread is a suite of strategies that connect Azure AD with Passport. In questo esempio di API REST tale strategia viene usata per i token di connessione.We use this strategy for bearer tokens in this REST API sample.

Nota

Anche se OAuth 2.0 fornisce un framework in cui è possibile rilasciare qualsiasi tipo di token noto, sono comunemente usati alcuni tipi di token.Although OAuth 2.0 provides a framework in which any known token type can be issued, certain token types are commonly used. I token di connessione vengono comunemente usati per proteggere gli endpoint.Bearer tokens are commonly used to protect endpoints. I token di connessione sono il tipo di token maggiormente rilasciato in OAuth 2.0.Bearer tokens are the most widely issued type of token in OAuth 2.0. Molte implementazioni di OAuth 2.0 presumono che i token di connessione siano l'unico tipo di token rilasciato.Many OAuth 2.0 implementations assume that bearer tokens are the only type of token issued.

  1. Al prompt dei comandi passare alla directory azuread.At a command prompt, change the directory to azuread.

    cd azuread

  2. Installare il modulo passport-azure-ad di Passport.js:Install the Passport.js passport-azure-ad module:

    npm install passport-azure-ad

    L'output di questo comando dovrebbe essere simile al seguente:The output of the command should look like this:

    passport-azure-ad@1.0.0 node_modules/passport-azure-ad
    ├── xtend@4.0.0
    ├── xmldom@0.1.19
    ├── passport-http-bearer@1.0.1 (passport-strategy@1.0.0)
    ├── underscore@1.8.3
    ├── async@1.3.0
    ├── jsonwebtoken@5.0.2
    ├── xml-crypto@0.5.27 (xpath.js@1.0.6)
    ├── ursa@0.8.5 (bindings@1.2.1, nan@1.8.4)
    ├── jws@3.0.0 (jwa@1.0.1, base64url@1.0.4)
    ├── request@2.58.0 (caseless@0.10.0, aws-sign2@0.5.0, forever-agent@0.6.1, stringstream@0.0.4, tunnel-agent@0.4.1, oauth-sign@0.8.0, isstream@0.1.2, extend@2.0.1, json-stringify-safe@5.0.1, node-uuid@1.4.3, qs@3.1.0, combined-stream@1.0.5, mime-types@2.0.14, form-data@1.0.0-rc1, http-signature@0.11.0, bl@0.9.4, tough-cookie@2.0.0, hawk@2.3.1, har-validator@1.8.0)
    └── xml2js@0.4.9 (sax@0.6.1, xmlbuilder@2.6.4)
    

7: Aggiungere i moduli MongoDB all'API Web7: Add MongoDB modules to your web API

Questo esempio usa MongoDB come archivio dati.In this sample, we use MongoDB as our data store.

  1. Installare Mongoose, un plug-in diffuso per la gestione di modelli e schemi:Install Mongoose, a widely used plug-in, to manage models and schemas:

    npm install mongoose

  2. Installare il driver del database per MongoDB, anche questo denominato MongoDB:Install the database driver for MongoDB, which is also called MongoDB:

    npm install mongodb

8. Installare moduli aggiuntivi8: Install additional modules

Installare gli altri moduli necessari.Install the remaining required modules.

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

  2. Immettere i comandi seguenti.Enter the following commands. I comandi installano i moduli seguenti nella directory node_modules:The commands install the following modules in your node_modules directory:

    • npm install crypto
    • npm install assert-plus
    • npm install posix-getopt
    • npm install util
    • npm install path
    • npm install connect
    • npm install xml-crypto
    • npm install xml2js
    • npm install xmldom
    • npm install async
    • npm install request
    • npm install underscore
    • npm install grunt-contrib-jshint@0.1.1
    • npm install grunt-contrib-nodeunit@0.1.2
    • npm install grunt-contrib-watch@0.2.0
    • npm install grunt@0.4.1
    • npm install xtend@2.0.3
    • npm install bunyan
    • npm update

9: Creare un file Server.js per le dipendenze9: Create a Server.js file for your dependencies

Il file Server.js fornirà la maggior parte della funzionalità per il server API Web.A Server.js file holds the majority of the functionality for your web API server. Aggiungere la maggior parte del codice a questo file.Add most of your code to this file. Per la produzione, è possibile eseguire il refactoring della funzionalità in file più piccoli, come per route e controller distinti.For production purposes, you can refactor the functionality into smaller files, like for separate routes and controllers. Questo articolo usa Server.js a questo scopo.In this article, we use Server.js for this purpose.

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

  2. Usando un editor di propria scelta, creare un file Server.js.Using an editor of your choice, create a Server.js file. Aggiungere al file le informazioni seguenti:Add the following information to the file:

    'use strict';
    /**
    * Module dependencies.
    */
    var util = require('util');
    var assert = require('assert-plus');
    var mongoose = require('mongoose/');
    var bunyan = require('bunyan');
    var restify = require('restify');
    var config = require('./config');
    var passport = require('passport');
    var OIDCBearerStrategy = require('passport-azure-ad').OIDCStrategy;
    
  3. Salvare il file.Save the file. Servirà ancora tra poco.You will return to it shortly.

10: Creare un file config per archiviare le impostazioni di Azure AD10: Create a config file to store your Azure AD settings

Questo file di codice passa i parametri di configurazione dal portale di Azure AD a Passport.js.This code file passes the configuration parameters from your Azure AD portal to Passport.js. Questi valori di configurazione sono stati creati quando si è aggiunta l'API Web al portale nella prima parte dell'articolo.You created these configuration values when you added the web API to the portal at the beginning of the article. Dopo aver copiato il codice, verrà spiegato che cosa inserire nei valori di questi parametri.After you copy the code, we'll explain what to put in the values of these parameters.

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

  2. In un editor creare un file Config.js.In an editor, create a Config.js file. Aggiungere le informazioni seguenti:Add the following information:

    // Don't commit this file to your public repos. This config is for first-run.
    exports.creds = {
    mongoose_auth_local: 'mongodb://localhost/tasklist', // Your Mongo auth URI goes here.
    issuer: 'https://sts.windows.net/**<your application id>**/',
    audience: '<your redirect URI>',
    identityMetadata: 'https://login.microsoftonline.com/common/.well-known/openid-configuration' // For Microsoft, you should never need to change this.
    };
    

Valori richiestiRequired values

  • IdentityMetadata: l'area in cui passport-azure-ad cercherà i dati di configurazione per il provider di identità (IDP), nonché le chiavi per la convalida dei token JSON Web (JWT).IdentityMetadata: This is where passport-azure-ad looks for your configuration data for the identity provider (IDP) and the keys to validate the JSON Web Tokens (JWTs). Se si usa Azure AD, probabilmente non si desidera modificare questa impostazione.If you are using Azure AD, you probably don't want to change this.

  • audience: URI di reindirizzamento dal portale.audience: Your redirect URI from the portal.

Nota

Le chiavi vengono registrate con una certa frequenza.Roll your keys at frequent intervals. Assicurarsi di eseguire sempre il pull dall'URL "openid_keys" e che l'app possa accedere a Internet.Be sure that you always pull from the "openid_keys" URL, and that the app can access the Internet.

11: Aggiungere la configurazione al file Server.js11: Add the configuration to your Server.js file

L'applicazione deve leggere questi valori dal file config appena creato.Your application needs to read the values from the config file you just created. Aggiungere il file config come risorsa necessaria nell'applicazione.Add the .config file as a required resource in your application. Impostare le variabili globali su quelle in Config.js.Set the global variables to those that are in Config.js.

  1. Al prompt dei comandi passare alla directory azuread:At the command prompt, change the directory to azuread:

    cd azuread

  2. In un editor aprire Server.js.In an editor, open Server.js. Aggiungere le informazioni seguenti:Add the following information:

    var config = require('./config');
    
  3. Aggiungere una nuova sezione a Server.js:Add a new section to Server.js:

    // Pass these options in to the ODICBearerStrategy.
    var options = {
    // The URL of the metadata document for your app. Put the keys for token validation from the URL found in the jwks_uri tag in the metadata.
    identityMetadata: config.creds.identityMetadata,
    issuer: config.creds.issuer,
    audience: config.creds.audience
    };
    // Array to hold signed-in users and the current signed-in user (owner).
    var users = [];
    var owner = null;
    // Your logger
    var log = bunyan.createLogger({
    name: 'Microsoft Azure Active Directory Sample'
    });
    

12: Aggiungere le informazioni su schemi e modelli MongoDB usando Mongoose12: Add the MongoDB model and schema information by using Mongoose

Successivamente, connettere questi tre file in un servizio API REST.Next, connect these three files in a REST API service.

Questo articolo usa MongoDB per archiviare le attività.In this article, we use MongoDB to store our tasks. Questa operazione viene illustrata nel passaggio 4.We discuss this in step 4.

Nel file Config.js creato nel passaggio 11 il database è denominato tasklist.In the Config.js file you created in step 11, your database is called tasklist. Questo database è stato inserito alla fine dell'URL di connessione mongoose_auth_local.That was what you put at the end of your mongoose_auth_local connection URL. Non è necessario creare in anticipo il database in MongoDB.You don't need to create this database beforehand in MongoDB. Il database viene creato alla prima esecuzione dell'applicazione server (presupponendo che il database non esista già).The database is created on the first run of your server application (assuming the database does not already exist).

Si è indicato al server quale database MongoDB usare.You've told the server what MongoDB database to use. Successivamente, è necessario scrivere codice aggiuntivo per creare il modello e lo schema per le attività del server.Next, you need to write some additional code to create the model and schema for your server's tasks.

Il modelloThe model

Il modello dello schema è molto semplice.The schema model is very basic. Se necessario, è possibile espanderlo.You can expand it if you need to.

Il modello dello schema presenta questi valori:The schema model has these values:

  • NAME.NAME. Persona assegnata all'attività.The person assigned to the task. Valore stringa.This is a string value.
  • TASK.TASK. Nome dell'attività.The name of the task. Valore stringa.This is a string value.
  • DATE.DATE. Data di scadenza dell'attività.The date that the task is due. Valore datetime.This is a datetime value.
  • COMPLETED.COMPLETED. Indica se l'attività è stata completata o meno.Whether the task is completed. Valore booleano.This is a Boolean value.

Creare lo schema nel codiceCreate the schema in the code

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

  2. Nell'editor aprire Server.js.In your editor, open Server.js. Sotto la voce di configurazione, aggiungere le informazioni seguenti:Below the configuration entry, add the following information:

    // MongoDB setup.
    // Set up some configuration.
    var serverPort = process.env.PORT || 8080;
    var serverURI = (process.env.PORT) ? config.creds.mongoose_auth_mongohq : config.creds.mongoose_auth_local;
    // Connect to MongoDB.
    global.db = mongoose.connect(serverURI);
    var Schema = mongoose.Schema;
    log.info('MongoDB Schema loaded');
    

Questo codice si connette al server MongoDB.This code connects to the MongoDB server. Restituisce inoltre un oggetto schema.It also returns a schema object.

Usando lo schema, creare il modello nel codiceUsing the schema, create your model in the code

Sotto il codice precedente aggiungere il codice seguente:Below the preceding code, add the following code:

// Create a basic schema to store your tasks and users.
var TaskSchema = new Schema({
owner: String,
task: String,
completed: Boolean,
date: Date
});
// Use the schema to register a model.
mongoose.model('Task', TaskSchema);
var Task = mongoose.model('Task');

Come si può vedere dal codice, per prima cosa viene creato lo schema.As you can tell from the code, first you create your schema. Si crea quindi un oggetto modello.Next, you create a model object. Usare l'oggetto modello per archiviare i dati in tutto il codice quando si definiscono le route.You use the model object to store your data throughout the code when you define your routes.

13: Aggiungere le route per il server API REST delle attività13: Add your routes for your task REST API server

Ora che è disponibile un modello di database con cui lavorare, aggiungere le route che verranno usate per il server API REST.Now that you have a database model to work with, add the routes you'll use for your REST API server.

Informazioni sulle route in RestifyAbout routes in restify

Le route in Restify funzionano esattamente come avviene quando si usa lo stack di Express.Routes in restify work exactly the same way they do when you use the Express stack. Definire le route usando l'URI che si prevede verrà chiamato dalle applicazioni client.You define routes by using the URI that you expect the client applications to call. Le route si definiscono di solito in un file separato.Usually, you define your routes in a separate file. In questa esercitazione, le route sono inserite in Server.js.In this tutorial, we put our routes in Server.js. È consigliabile fattorizzarle nel relativo file per usarle in fase di produzione.For production use, we recommend that you factor routes into their own file.

Un modello tipico per una route Restify è:A typical pattern for a restify route is:

function createObject(req, res, next) {
// Do work on object.
_object.name = req.params.object; // Passed value is in req.params under object.
///...
return next(); // Keep the server going.
}
....
server.post('/service/:add/:object', createObject); // calls createObject on routes that match this.

Questo è il modello più semplice.This is the pattern at the most basic level. Restify ed Express offrono funzionalità molto più avanzate come la definizione di tipi di applicazione e il routing complesso tra endpoint diversi.Restify (and Express) provide much deeper functionality, like the ability to define application types, and complex routing across different endpoints.

Aggiungere le route predefinite al serverAdd default routes to your server

Aggiungere le route CRUD di base: creazione, recupero, aggiornamento ed eliminazione.Add the basic CRUD routes: create, retrieve, update, and delete.

  1. Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

    cd azuread

  2. In un editor aprire Server.js.In an editor, open Server.js. Aggiungere le informazioni seguenti sotto le voci di database create in precedenza:Below the database entries you made earlier, add the following information:

    /**
    *
    * APIs for your REST task server
    */
    // Create a task.
    function createTask(req, res, next) {
    // Resitify currently has a bug that doesn't allow you to set default headers.
    // These headers comply with CORS, and allow you to use MongoDB Server as your response to any origin.
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    // Create a new task model, fill it, and save it to MongoDB.
    var _task = new Task();
    if (!req.params.task) {
    req.log.warn({
    params: p
    }, 'createTodo: missing task');
    next(new MissingTaskError());
    return;
    }
    _task.owner = owner;
    _task.task = req.params.task;
    _task.date = new Date();
    _task.save(function(err) {
    if (err) {
    req.log.warn(err, 'createTask: unable to save');
    next(err);
    } else {
    res.send(201, _task);
    }
    });
    return next();
    }
    // Delete a task by name.
    function removeTask(req, res, next) {
    Task.remove({
    task: req.params.task,
    owner: owner
    }, function(err) {
    if (err) {
    req.log.warn(err,
    'removeTask: unable to delete %s',
    req.params.task);
    next(err);
    } else {
    log.info('Deleted task:', req.params.task);
    res.send(204);
    next();
    }
    });
    }
    // Delete all tasks.
    function removeAll(req, res, next) {
    Task.remove();
    res.send(204);
    return next();
    }
    // Get a specific task based on name.
    function getTask(req, res, next) {
    log.info('getTask was called for: ', owner);
    Task.find({
    owner: owner
    }, function(err, data) {
    if (err) {
    req.log.warn(err, 'get: unable to read %s', owner);
    next(err);
    return;
    }
    res.json(data);
    });
    return next();
    }
    /// Returns the list of TODOs that were loaded.
    function listTasks(req, res, next) {
    // Resitify currently has a bug that doesn't allow you to set default headers.
    // These headers comply with CORS, and allow us to use MongoDB Server as our response to any origin.
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    log.info("listTasks was called for: ", owner);
    Task.find({
    owner: owner
    }).limit(20).sort('date').exec(function(err, data) {
    if (err)
    return next(err);
    if (data.length > 0) {
    log.info(data);
    }
    if (!data.length) {
    log.warn(err, "There are no tasks in the database. Add one!");
    }
    if (!owner) {
    log.warn(err, "You did not pass an owner when listing tasks.");
    } else {
    res.json(data);
    }
    });
    return next();
    }
    

Aggiungere la gestione di errori per le routeAdd error handling for the routes

Aggiungere la gestione di errori per poter comunicare al client il problema che si è verificato.Add some error handling so you can communicate back to the client about the problem you encountered.

Aggiungere il codice seguente sotto il codice già scritto:Add the following code below the code, which you've already written:

///--- Errors for communicating something more information back to the client.
function MissingTaskError() {
restify.RestError.call(this, {
statusCode: 409,
restCode: 'MissingTask',
message: '"task" is a required parameter',
constructorOpt: MissingTaskError
});
this.name = 'MissingTaskError';
}
util.inherits(MissingTaskError, restify.RestError);
function TaskExistsError(owner) {
assert.string(owner, 'owner');
restify.RestError.call(this, {
statusCode: 409,
restCode: 'TaskExists',
message: owner + ' already exists',
constructorOpt: TaskExistsError
});
this.name = 'TaskExistsError';
}
util.inherits(TaskExistsError, restify.RestError);
function TaskNotFoundError(owner) {
assert.string(owner, 'owner');
restify.RestError.call(this, {
statusCode: 404,
restCode: 'TaskNotFound',
message: owner + ' was not found',
constructorOpt: TaskNotFoundError
});
this.name = 'TaskNotFoundError';
}
util.inherits(TaskNotFoundError, restify.RestError);

14: Creare il server14: Create your server

L'ultima operazione da eseguire consiste nell'aggiungere l'istanza del server.The last thing to do is to add your server instance. L'istanza del server gestisce le chiamate.The server instance manages your calls.

Restify ed Express hanno un elevato livello di personalizzazione che è possibile usare con un server API REST.Restify (and Express) have deep customization that you can use with a REST API server. Per questa esercitazione si userà la configurazione più semplice.In this tutorial, we use the most basic setup.

/**
* Your server
*/
var server = restify.createServer({
name: "Microsoft Azure Active Directory TODO Server",
version: "2.0.1"
});
// Ensure that you don't drop data on uploads.
server.pre(restify.pre.pause());
// Clean up imprecise paths like //todo//////1//.
server.pre(restify.pre.sanitizePath());
// Handle annoying user agents (curl).
server.pre(restify.pre.userAgentConnection());
// Set a per-request Bunyan logger (with requestid filled in).
server.use(restify.requestLogger());
// Allow 5 requests/second by IP address, and burst to 10.
server.use(restify.throttle({
burst: 10,
rate: 5,
ip: true,
}));
// Use common commands, such as:
server.use(restify.acceptParser(server.acceptable));
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.gzipResponse());
server.use(restify.bodyParser({
mapParams: true
}));

15: Aggiungere le route (per ora senza autenticazione)15: Add the routes (without authentication, for now)

/// Use CRUD to add the real handlers.
/**
/*
/* Each of these handlers is protected by your Open ID Connect Bearer strategy. Invoke 'oidc-bearer'
/* in the pasport.authenticate() method. Because REST is stateless, set 'session: false'. You 
/* don't need to maintain session state. You can experiment with removing API protection.
/* To do this, remove the passport.authenticate() method:
/*
/* server.get('/tasks', listTasks);
/*
**/
server.get('/tasks', listTasks);
server.get('/tasks', listTasks);
server.get('/tasks/:owner', getTask);
server.head('/tasks/:owner', getTask);
server.post('/tasks/:owner/:task', createTask);
server.post('/tasks', createTask);
server.del('/tasks/:owner/:task', removeTask);
server.del('/tasks/:owner', removeTask);
server.del('/tasks', removeTask);
server.del('/tasks', removeAll, function respond(req, res, next) {
res.send(204);
next();
});
// Register a default '/' handler
server.get('/', function root(req, res, next) {
var routes = [
'GET /',
'POST /tasks/:owner/:task',
'POST /tasks (for JSON body)',
'GET /tasks',
'PUT /tasks/:owner',
'GET /tasks/:owner',
'DELETE /tasks/:owner/:task'
];
res.send(200, routes);
next();
});
server.listen(serverPort, function() {
var consoleMessage = '\n Microsoft Azure Active Directory Tutorial';
consoleMessage += '\n +++++++++++++++++++++++++++++++++++++++++++++++++++++';
consoleMessage += '\n %s server is listening at %s';
consoleMessage += '\n Open your browser to %s/tasks\n';
consoleMessage += '+++++++++++++++++++++++++++++++++++++++++++++++++++++ \n';
consoleMessage += '\n !!! why not try a $curl -isS %s | json to get some ideas? \n';
consoleMessage += '+++++++++++++++++++++++++++++++++++++++++++++++++++++ \n\n';
});

16: Eseguire il server16: Run the server

È consigliabile eseguire il test del server prima di aggiungere l'autenticazione.It's a good idea to test your server before you add authentication.

Il modo più semplice per farlo consiste nell'usare Curl al prompt dei comandi.The easiest way to test your server is by using curl at a command prompt. È necessaria una semplice utilità che consente di analizzare l'output come JSON.To do this, you need a simple utility that you can use to parse output as JSON.

  1. Installare lo strumento JSON usato negli esempi seguenti:Install the JSON tool that we use in the following examples:

    $npm install -g jsontool

    Questo comando installa lo strumento JSON a livello globale.This installs the JSON tool globally.

  2. Assicurarsi che l'istanza di MongoDB sia in esecuzione.Make sure that your MongoDB instance is running:

    $sudo mongod

  3. Passare alla directory azuread e quindi eseguire Curl:Change the directory to azuread, and then run curl:

    $ cd azuread $ node server.js

    $ curl -isS http://127.0.0.1:8080 | json

    HTTP/1.1 2.0OK
    Connection: close
    Content-Type: application/json
    Content-Length: 171
    Date: Tue, 14 Jul 2015 05:43:38 GMT
    [
    "GET /",
    "POST /tasks/:owner/:task",
    "POST /tasks (for JSON body)",
    "GET /tasks",
    "PUT /tasks/:owner",
    "GET /tasks/:owner",
    "DELETE /tasks/:owner/:task"
    ]
    
  4. Per aggiungere un'attività:To add a task:

    $ curl -isS -X POST http://127.0.0.1:8888/tasks/brandon/Hello

    La risposta dovrebbe essere:The response should be:

    HTTP/1.1 201 Created
    Connection: close
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Headers: X-Requested-With
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 5
    Date: Tue, 04 Feb 2014 01:02:26 GMT
    Hello
    
  5. Elenco delle attività Brandon:List tasks for Brandon:

    $ curl -isS http://127.0.0.1:8080/tasks/brandon/

Se tutti questi comandi vengono eseguiti senza errori, è possibile aggiungere OAuth al server dell'API REST.If all these commands run without errors, you are ready to add OAuth to the REST API server.

Ora si dispone di un server API REST con MongoDB.You now have a REST API server with MongoDB!

17: Aggiungere l'autenticazione al server API REST17: Add authentication to your REST API server

Ora che l'API REST è in esecuzione, è possibile configurarla per usarla in Azure AD.Now that you have a running REST API, set it up to use it with Azure AD.

Al prompt dei comandi passare alla directory azuread:At a command prompt, change the directory to azuread:

cd azuread

Usare l'oggetto oidcbearerstrategy incluso in passport-azure-adUse the oidcbearerstrategy that's included with passport-azure-ad

Nei passaggi precedenti è stato creato un tipico server REST TODO senza alcun tipo di autenticazione.So far, you've built a typical REST TODO server without any kind of authorization. Aggiungere l'autenticazione.Now, add authentication.

In primo luogo, indicare che si desidera usare Passport.First, indicate that you want to use Passport. Inserire il codice subito dopo la configurazione del server precedente:Put this right after your earlier server configuration:

// Start using Passport.js.

server.use(passport.initialize()); // Starts passport
server.use(passport.session()); // Provides session support

Suggerimento

Durante la scrittura delle API è sempre consigliabile collegare i dati a un elemento univoco del token di cui l'utente non può eseguire lo spoofing.When you write APIs, it's a good idea to always link the data to something unique from the token that the user can’t spoof. Quando archivia gli elementi TODO, il server esegue questa operazione in base all'ID sottoscrizione dell'utente nel token (chiamato mediante token.sub).When this server stores TODO items, it stores them based on the user subscription ID in the token (called through token.sub). Inserire il token.sub nel campo "owner".You put the token.sub in the “owner” field. In questo modo, solo l'utente può accedere ai propri elementi TODO.This ensures that only that user can access the user's TODOs. Nessun altro può accedere agli elementi TODO immessi.No one else can access the TODOs that were entered. Nell'API di "owner" non è presente alcuna esposizione.There is no exposure in the API for “owner.” Un utente esterno può richiedere gli elementi TODO di altri utenti, anche se sono autenticati.An external user can request other users' TODOs, even if they are authenticated.

Usare quindi la strategia Open ID Connect Bearer fornita con passport-azure-ad.Next, use the Open ID Connect Bearer strategy that comes with passport-azure-ad. Inserire il codice seguente dopo quanto inserito in precedenza:Put this after what you pasted earlier:

/**
/*
/* Calling the OIDCBearerStrategy and managing users.
/*
/* Because of the Passport pattern, you need to manage users and info tokens
/* with a FindorCreate() method. The method must be provided by the implementor.
/* In the following code, you autoregister any user and implement a FindById().
/* It's a good idea to do something more advanced.
**/
var findById = function(id, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
if (user.sub === id) {
log.info('Found user: ', user);
return fn(null, user);
}
}
return fn(null, null);
};
var oidcStrategy = new OIDCBearerStrategy(options,
function(token, done) {
log.info('verifying the user');
log.info(token, 'was the token retrieved');
findById(token.sub, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
// "Auto-registration"
log.info('User was added automatically, because they were new. Their sub is: ', token.sub);
users.push(token);
owner = token.sub;
return done(null, token);
}
owner = token.sub;
return done(null, user, token);
});
}
);
passport.use(oidcStrategy);

Passport usa un modello simile per tutte le strategie (Twitter, Facebook e così via).Passport uses a similar pattern for all its strategies (Twitter, Facebook, and so on). Tutti i writer di strategie rispettano questo modello.All strategy writers adhere to the pattern. Passare alla strategia un elemento function() che usa un token e done come parametri.Pass the strategy a function() that uses a token and done as parameters. La strategia viene restituita dopo l'esecuzione di tutte le operazioni.The strategy is returned after it does all its work. Archiviare l'utente e mettere da parte il token per non doverlo richiedere nuovamente.Store the user and stash the token so you don’t need to ask for it again.

Importante

Il codice precedente accetta qualsiasi utente che può eseguire l'autenticazione al server.The preceding code takes any user that can authenticate to your server. Questa operazione è nota come registrazione automatica.This is known as auto-registration. Nei server di produzione è preferibile non consentire l'accesso a chiunque senza prima prevedere un processo di registrazione a scelta.On a production server, you wouldn’t want to let anyone in without first having them go through a registration process that you choose. Questo è il modello in genere adottato per le app consumer.This is usually the pattern you see in consumer apps. L'app potrebbe consentire di eseguire la registrazione con Facebook, ma poi richiede di inserire informazioni aggiuntive.The app might allow you to register with Facebook, but then it asks you to enter additional information. Se per questa esercitazione non è stato usato un programma della riga di comando, è possibile estrarre il messaggio di posta elettronica dall'oggetto token restituito.If you weren't using a command-line program for this tutorial, you could extract the email from the token object that is returned. Quindi, è possibile chiedere all'utente di inserire informazioni aggiuntive.Then, you might ask the user to enter additional information. Trattandosi di un server di test, è possibile aggiungere l'utente direttamente al database in-memory.Because this is a test server, you add the user directly to the in-memory database.

Proteggere gli endpointProtect endpoints

Proteggere gli endpoint, specificando la chiamata a passport.authenticate() con il protocollo preferito.Protect endpoints by specifying the passport.authenticate() call with the protocol that you want to use.

È possibile modificare la route nel codice del server per un'opzione più avanzata:You can edit your route in your server code for a more advanced option:

server.get('/tasks', passport.authenticate('oidc-bearer', {
session: false
}), listTasks);
server.get('/tasks', passport.authenticate('oidc-bearer', {
session: false
}), listTasks);
server.get('/tasks/:owner', passport.authenticate('oidc-bearer', {
session: false
}), getTask);
server.head('/tasks/:owner', passport.authenticate('oidc-bearer', {
session: false
}), getTask);
server.post('/tasks/:owner/:task', passport.authenticate('oidc-bearer', {
session: false
}), createTask);
server.post('/tasks', passport.authenticate('oidc-bearer', {
session: false
}), createTask);
server.del('/tasks/:owner/:task', passport.authenticate('oidc-bearer', {
session: false
}), removeTask);
server.del('/tasks/:owner', passport.authenticate('oidc-bearer', {
session: false
}), removeTask);
server.del('/tasks', passport.authenticate('oidc-bearer', {
session: false
}), removeTask);
server.del('/tasks', passport.authenticate('oidc-bearer', {
session: false
}), removeAll, function respond(req, res, next) {
res.send(204);
next();
});

18: Eseguire nuovamente l'applicazione server18: Run your server application again

Per verificare se la protezione OAuth 2.0 per gli endpoint è attiva, usare nuovamente Curl.Use curl again to see if you have OAuth 2.0 protection against your endpoints. Eseguire questa operazione prima di usare uno qualsiasi degli SDK client in questo endpoint.Do this before you run any of your client SDKs against this endpoint. Le intestazioni restituite dovrebbero indicare se l'autenticazione funziona correttamente.The headers returned should tell you if your authentication is working correctly.

  1. Assicurarsi che l'istanza di MongoDB sia in esecuzione:Make sure that your MongoDB isntance is running:

    $sudo mongod

  2. Passare alla directory azuread, quindi usare Curl:Change to the azuread directory, and then use curl:

    $ cd azuread

    $ node server.js

  3. Provare un'operazione POST di base:Try a basic POST:

    $ curl -isS -X POST http://127.0.0.1:8080/tasks/brandon/Hello

    HTTP/1.1 401 Unauthorized
    Connection: close
    WWW-Authenticate: Bearer realm="Users"
    Date: Tue, 14 Jul 2015 05:45:03 GMT
    Transfer-Encoding: chunked
    

Una risposta 401 indica che il livello Passport sta tentando il reindirizzamento all'endpoint di autorizzazione, ovvero il comportamento voluto.A 401 response indicates that the Passport layer is trying to redirect to the authorize endpoint, which is exactly what you want.

Ora è disponibile un servizio API REST che usa OAuth 2.0.You now have a REST API service that uses OAuth 2.0!

Sono state eseguite tutte le operazioni possibili con questo server senza usare un client compatibile con OAuth 2.0.You've gone as far as you can with this server without using an OAuth 2.0-compatible client. A tale scopo, è necessario esaminare un'esercitazione aggiuntiva.For that, you will need to review an additional tutorial.

Passaggi successiviNext steps

Come riferimento viene fornito l'esempio completato, senza i valori di configurazione, come file zip.For reference, the completed sample (without your configuration values) is provided as a .zip file. È anche possibile clonarlo da GitHub:You also can clone it from GitHub:

git clone --branch complete https://github.com/AzureADQuickStarts/AppModelv2-WebAPI-nodejs.git

Ora è possibile passare ad argomenti più avanzati.Now, you can move on to more advanced topics. Si potrebbe volere proteggere un'app Web Node.js usando l'endpoint v2.0.You might want to try Secure a Node.js web app using the v2.0 endpoint.

Altre risorse:Here are some additional resources:

Ottenere aggiornamenti della sicurezza per i prodottiGet security updates for our products

È consigliabile eseguire l'iscrizione per ricevere una notifica quando si verificano problemi di protezione.We encourage you to sign up to be notified when security incidents occur. Nella pagina Notifiche sulla sicurezza Microsoft effettuare la sottoscrizione agli advisory sulla sicurezza.On the Microsoft Technical Security Notifications page, subscribe to Security Advisories Alerts.