Azure AD B2C: proteggere un'API Web usando Node.jsAzure AD B2C: Secure a web API by using Node.js

Azure Active Directory (Azure AD) B2C permette di proteggere un'API Web usando i token di accesso OAuth 2.0.With Azure Active Directory (Azure AD) B2C, you can secure a web API by using OAuth 2.0 access tokens. I token di accesso consentono alle app client che usano Azure AD B2C di eseguire l'autenticazione all'API.These tokens allow your client apps that use Azure AD B2C to authenticate to the API. Questo articolo illustra come creare un'API di elenco attività che consenta agli utenti di aggiungere ed elencare attività.This article shows you how to create a "to-do list" API that allows users to add and list tasks. L'API Web è protetta con Azure AD B2C e consente soltanto agli utenti autenticati di gestire il proprio elenco attività.The web API is secured using Azure AD B2C and only allows authenticated users to manage their to-do list.

Nota

Questo esempio è stato scritto per l' applicazione di esempio iOS B2C.This sample was written to be connected to by using our iOS B2C sample application. Eseguire prima questa procedura dettagliata e quindi proseguire con l'esempio.Do the current walk-through first, and then follow along with that sample.

Passport è il middleware di autenticazione per Node.js.Passport is authentication middleware for Node.js. Estremamente flessibile e modulare, Passport può essere installato in modo non invadente in qualsiasi applicazione Web basata su Express o Restify.Flexible and modular, Passport can be unobtrusively installed in any Express-based or Restify web application. Un set completo di strategie supporta l'autenticazione tramite nome utente e password, Facebook, Twitter e altro ancora.A comprehensive set of strategies supports authentication by using a user name and password, Facebook, Twitter, and more. È stata sviluppata una strategia per Azure Active Directory (Azure AD).We have developed a strategy for Azure Active Directory (Azure AD). Dopo l'installazione di questo modulo, aggiungere il plug-in passport-azure-ad di Azure AD.You install this module and then add the Azure AD passport-azure-ad plug-in.

Per questo esempio è necessario:To do this sample, you need to:

  1. Registrare un'applicazione con Azure AD.Register an application with Azure AD.
  2. Configurare l'applicazione per l'uso del plug-in passport-azure-ad di Passport.Set up your application to use Passport's passport-azure-ad plug-in.
  3. Configurare un'applicazione client per chiamare l'API Web To Do List.Configure a client application to call the "to-do list" web API.

Ottenere una directory di Azure AD B2CGet an Azure AD B2C directory

Prima di poter usare Azure AD B2C, è necessario creare una directory, o tenant.Before you can use Azure AD B2C, you must create a directory, or tenant. Una directory è un contenitore per utenti, app, gruppi e altro ancora.A directory is a container for all users, apps, groups, and more. Se non ne è già disponibile una, creare una directory B2C prima di continuare.If you don't have one already, create a B2C directory before you continue.

Creare un'applicazioneCreate an application

A questo punto è necessario creare un'app nella directory B2C, che fornisce ad Azure AD alcune informazioni necessarie per comunicare in modo sicuro con l'app.Next, you need to create an app in your B2C directory that gives Azure AD some information that it needs to securely communicate with your app. In questo caso, sia l'app client che l'API Web sono rappresentate da un unico ID applicazione perché includono una sola app per la logica.In this case, both the client app and web API are represented by a single Application ID, because they comprise one logical app. Per creare un'app, seguire questa procedura.To create an app, follow these instructions. Assicurarsi di:Be sure to:

  • Includere un' app Web/API Web nell'applicazioneInclude a web app/web api in the application
  • Immettere http://localhost/TodoListService come URL di risposta.Enter http://localhost/TodoListService as a Reply URL. Si tratta dell'URL predefinito per questo esempio di codice.It is the default URL for this code sample.
  • Creare un segreto applicazione per l'applicazione e prenderne nota.Create an Application secret for your application and copy it. Questi dati saranno necessari in un secondo momento.You need this data later. Si noti che prima di usare questo valore è necessario inserire un carattere di escape XML .Note that this value needs to be XML escaped before you use it.
  • Copiare l' ID applicazione assegnato all'app.Copy the Application ID that is assigned to your app. Questi dati saranno necessari in un secondo momento.You need this data later.

Importante

Non è possibile usare le applicazioni registrate nella scheda Applicazioni del portale di gestione di Azure per questa operazione.You cannot use applications registered in the Applications tab on the classic Azure Management Portal for this.

Creare i criteriCreate your policies

In Azure AD B2C ogni esperienza utente è definita da criterispecifici.In Azure AD B2C, every user experience is defined by a policy. Questa app contiene due esperienze di identità: iscrizione e accesso.This app contains two identity experiences: sign up and sign in. È necessario creare i criteri per ogni tipo, come descritto nell' articolo di riferimento per i criteri.You need to create one policy of each type, as described in the policy reference article. Durante la creazione dei tre criteri assicurarsi di:When you create your three policies, be sure to:

  • Scegliere Nome visualizzato e altri attributi nei criteri di iscrizione.Choose the Display name and other sign-up attributes in your sign-up policy.
  • Scegliere le attestazioni dell'applicazione Nome visualizzato e ID oggetto in tutti i criteri.Choose the Display name and Object ID application claims in every policy. È consentito scegliere anche altre attestazioni.You can choose other claims as well.
  • Copiare il Nome di ciascun criterio dopo averlo creato.Copy down the Name of each policy after you create it. Dovrebbero mostrare il prefisso b2c_1_.It should have the prefix b2c_1_. I nomi dei criteri saranno necessari in un secondo momento.You need those policy names later.

Nota

In Azure AD B2C, il nome del criterio verrà preceduto da b2c_1_, come b2c_1_sign_up.In Azure AD B2C, your policy's name will be prefixed with b2c_1_, like b2c_1_sign_up. Si è liberi di utilizzare i criteri in tutte le app, sia client che server.You are free to use your policies across all of your apps, both client and server. Se sono stati creati in precedenza criteri in un altra procedura B2C, non è necessario procedere nuovamente.If you've previously created policies in another B2C walk-through, there is no need to do so again. È possibile riutilizzare i criteri creati in precedenza nel portale se corrispondono ai requisiti dell'applicazione.You may reuse the policies you've previously created in the portal if they match the requirements of the application.

Dopo aver creato i tre criteri, è possibile passare alla compilazione dell'app.After you have created your three policies, you're ready to build your app.

Per informazioni sul funzionamento dei criteri in Azure AD B2C, iniziare dall' esercitazione introduttiva per la compilazione di un'app Web .NET.To learn about how policies work in Azure AD B2C, start with the .NET web app getting started tutorial.

Scaricare il codiceDownload the code

Il codice per questa esercitazione è disponibile in GitHub.The code for this tutorial is maintained on GitHub. Per compilare l'esempio passo dopo passo, è possibile scaricare un progetto bozza come file ZIP.To build the sample as you go, you can download a skeleton project as a .zip file. È anche possibile clonare la struttura:You can also clone the skeleton:

git clone --branch skeleton https://github.com/AzureADQuickStarts/B2C-WebAPI-NodeJS.git

L'app completata è anche disponibile come file ZIP o nel ramo complete dello stesso repository.The completed app is also available as a .zip file or on the complete branch of the same repository.

Scaricare node.js per la piattaforma correnteDownload Node.js for your platform

Per usare correttamente questo esempio è necessaria un'installazione funzionante di Node.js.To successfully use this sample, you need a working installation of Node.js.

Installare Node.js da nodejs.org.Install Node.js from nodejs.org.

Installare MongoDB per la piattaforma correnteInstall MongoDB for your platform

Per usare correttamente questo esempio è necessaria un'installazione funzionante di MongoDB.To successfully use this sample, you need a working installation of MongoDB. MongoDB viene usato per rendere l'API REST persistente nelle istanze del server.We use MongoDB to make your REST API persistent across server instances.

Installare MongoDB da mongodb.org.Install MongoDB from mongodb.org.

Nota

In questa procedura dettagliata si presume che si usino gli endpoint server e di installazione predefiniti per MongoDB, al momento della stesura di questo articolo mongodb://localhost.This walk-through assumes that you use the default installation and server endpoints for MongoDB, which at the time of this writing is mongodb://localhost.

Installare i moduli Restify nell'API WebInstall the Restify modules in your web API

Restify verrà usato per compilare l'API REST.We use Restify to build your 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 derived from Express. che include una gamma completa di funzionalità per la compilazione di API REST basate su Connect.It has a robust set of features for building REST APIs on top of Connect.

Installare RestifyInstall Restify

Dalla riga di comando passare alla directory azuread.From the command line, change your directory to azuread. Se la directory azuread non esiste, crearla.If the azuread directory doesn't exist, create it.

cd azuread oppure mkdir azuread;cd azuread or mkdir azuread;

Immettere il comando seguente:Enter the following command:

npm install restify

Questo comando installa Restify.This command installs Restify.

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

In alcuni sistemi operativi, quando si usa npm potrebbero essere visualizzati l'errore Error: EPERM, chmod '/usr/local/bin/..' e una richiesta di eseguire l'account come amministratore.In some operating systems, when you use npm, you may receive the error Error: EPERM, chmod '/usr/local/bin/..' and a request that you run the account as an administrator. In tal caso, usare il comando sudo per eseguire npm a un livello di privilegi più elevato.If this problem occurs, use the sudo command to run npm at a higher privilege level.

È stato visualizzato un errore di DTrace?Did you get a DTrace error?

Durante l'installazione di Restify potrebbe essere visualizzato un testo simile al seguente:You may see something like this text when you install Restify:

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: 2
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 offre un meccanismo efficace per tenere traccia delle chiamate REST usando DTrace.Restify provides a powerful mechanism for tracing REST calls by using DTrace. Tuttavia, per molti sistemi operativi DTrace non è disponibile.However, many operating systems do not have DTrace available. È possibile ignorare questi errori.You can safely ignore these errors.

L'output del comando avrà un aspetto simile al seguente:The output of the command should appear similar to this text:

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)

Installare Passport nell'API WebInstall Passport in your web API

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente.From the command line, change your directory to azuread, if it's not already there.

Installare Passport usando il comando seguente:Install Passport using the following command:

npm install passport

L'output del comando sarà simile al seguente:The output of the command should be similar to this text:

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

Aggiungere passport-azuread all'API WebAdd passport-azuread to your web API

A questo punto aggiungere la strategia di OAuth usando passport-azuread, una suite di strategie che connettono Azure AD a Passport.Next, add the OAuth strategy by using passport-azuread, a suite of strategies that connect Azure AD with Passport. Usare questa strategia per i token di connessione nell'esempio di API REST.Use this strategy for bearer tokens in the REST API sample.

Nota

Anche se OAuth2 fornisce un framework in cui è possibile rilasciare qualsiasi tipo di token noto, solo determinati tipi di token sono usati su larga scala.Although OAuth2 provides a framework in which any known token type can be issued, only certain token types have gained widespread use. I token per la protezione degli endpoint sono token di connessioneThe tokens for protecting endpoints are bearer tokens. e sono i tipi di token maggiormente rilasciati in OAuth2.These types of tokens are the most widely issued in OAuth2. Molte implementazioni presumono che i token di connessione sono l'unico tipo di token rilasciato.Many implementations assume that bearer tokens are the only type of token issued.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente.From the command line, change your directory to azuread, if it's not already there.

Immettere il comando seguente per installare il modulo Passport passport-azure-ad :Install the Passport passport-azure-ad module using the following command:

npm install passport-azure-ad

L'output del comando sarà simile al seguente:The output of the command should be similar to this text:

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)

Aggiungere i moduli MongoDB all'API WebAdd MongoDB modules to your web API

Questo esempio usa MongoDB come archivio dati.This sample uses MongoDB as your data store. A tale scopo installare Mongoose, un plug-in diffuso per la gestione di modelli e schemi.For that install Mongoose, a widely used plug-in for managing models and schemas.

  • npm install mongoose

Installare moduli aggiuntiviInstall additional modules

A questo punto, installare gli altri moduli necessari.Next, install the remaining required modules.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Installare i moduli nella directory node_modules :Install the modules in your node_modules directory:

  • npm install assert-plus
  • npm install ejs
  • npm install ejs-locals
  • npm install express
  • npm install bunyan

Creare un file server.js con le dipendenzeCreate a server.js file with your dependencies

Il file server.js offre la maggior parte della funzionalità per il server API Web.The server.js file provides the majority of the functionality for your Web API server.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Creare un file server.js in un editor.Create a server.js file in an editor. Aggiungere le informazioni seguenti:Add the following information:

'use strict';
/**
* Module dependencies.
*/
var fs = require('fs');
var path = require('path');
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').BearerStrategy;

Salvare il file.Save the file. Servirà ancora successivamente.You return to it later.

Creare un file config.js per archiviare le impostazioni di Azure ADCreate a config.js file to store your Azure AD settings

Questo file di codice passa i parametri di configurazione dal portale di Azure AD al file Passport.js .This code file passes the configuration parameters from your Azure AD Portal to the Passport.js file. Questi valori di configurazione sono stati creati quando si è aggiunta l'API Web al portale nella prima parte della procedura dettagliata.You created these configuration values when you added the web API to the portal in the first part of the walk-through. Dopo aver copiato il codice, verrà spiegato che cosa inserire nei valori di questi parametri.We explain what to put in the values of these parameters after you copy the code.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Creare un file config.js in un editor.Create a config.js file in an editor. 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 = {
clientID: <your client ID for this Web API you created in the portal>
mongoose_auth_local: 'mongodb://localhost/tasklist', // Your mongo auth uri goes here
audience: '<your audience URI>', // the Client ID of the application that is calling your API, usually a web API or native client
identityMetadata: 'https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration', // Make sure you add the B2C tenant name in the <tenant name> area
tenantName:'<tenant name>',
policyName:'b2c_1_<sign in policy name>' // This is the policy you'll want to validate against in B2C. Usually this is your Sign-in policy (as users sign in to this API)
passReqToCallback: false // This is a node.js construct that lets you pass the req all the way back to any upstream caller. We turn this off as there is no upstream caller.
};

Nota

Il nome del tenant B2C è il dominio immesso durante la creazione del tenant e viene visualizzato nel pannello della directory nel portale di Azure.Your B2C tenant's name is the domain that you entered during tenant creation, and is displayed on the directory blade in the Azure portal. In genere termina con il suffisso .onmicrosoft.com, ad esempio, contosob2c.onmicrosoft.com.It usually ends with the suffix .onmicrosoft.com, for instance, contosob2c.onmicrosoft.com.

Valori richiestiRequired values

clientID: ID client dell'applicazione API Web.clientID: The client ID of your Web API application.

IdentityMetadata: qui passport-azure-ad cerca i dati di configurazione per il provider di identitàIdentityMetadata: This is where passport-azure-ad looks for your configuration data for the identity provider. e le chiavi di convalida dei token JSON Web.It also looks for the keys to validate the JSON web tokens.

audience: URI (Uniform Resource Identifier) dal portale che identifica l'applicazione chiamante.audience: The uniform resource identifier (URI) from the portal that identifies your calling application.

tenantName: nome del tenant, ad esempio contoso.onmicrosoft.com.tenantName: Your tenant name (for example, contoso.onmicrosoft.com).

policyName: criteri da usare per convalidare i token in ingresso nel server.policyName: The policy that you want to validate the tokens coming in to your server. Usare gli stessi criteri usati nell'applicazione client per l'accesso.This policy should be the same policy that you use on the client application for sign-in.

Nota

Per questo esempio, usare gli stessi i criteri per entrambe le configurazioni client e server.For now, use the same policies across both client and server setup. Se è già stata completata una procedura dettagliata in cui sono stati creati questi criteri, non è necessario crearli di nuovo.If you have already completed a walk-through and created these policies, you don't need to do so again. Dal momento che la procedura dettagliata è stata completata, non è necessario impostare nuovi criteri per procedure dettagliate relative ai client nel sito.Because you completed the walk-through, you shouldn't need to set up new policies for client walk-throughs on the site.

Aggiungere la configurazione al file server.jsAdd configuration to your server.js file

Per leggere i valori dal file config.js creato, aggiungere il file .config come risorsa necessaria nell'applicazione e quindi impostare le variabili globali su quelle del documento config.js.To read the values from the config.js file you created, add the .config file as a required resource in your application, and then set the global variables to those in the config.js document.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Aprire il file server.js in un editor.Open the server.js file in an editor. Aggiungere le informazioni seguenti:Add the following information:

var config = require('./config');

Aggiungere una nuova sezione a server.js con il codice seguente:Add a new section to server.js that includes the following code:

// We pass these options in to the ODICBearerStrategy.

var options = {
    // The URL of the metadata document for your app. We put the keys for token validation from the URL found in the jwks_uri tag of the in the metadata.
    identityMetadata: config.creds.identityMetadata,
    clientID: config.creds.clientID,
    tenantName: config.creds.tenantName,
    policyName: config.creds.policyName,
    validateIssuer: config.creds.validateIssuer,
    audience: config.creds.audience,
    passReqToCallback: config.creds.passReqToCallback

};

Aggiungere quindi alcuni segnaposto per gli utenti ricevuti dalle applicazioni chiamanti.Next, let's add some placeholders for the users we receive from our calling applications.

// array to hold logged in users and the current logged in user (owner)
var users = [];
var owner = null;

Creare anche il logger.Let's go ahead and create our logger too.

// Our logger
var log = bunyan.createLogger({
    name: 'Microsoft Azure Active Directory Sample'
});

Aggiungere le informazioni su schemi e modelli MongoDB usando MoongooseAdd the MongoDB model and schema information by using Mongoose

Le attività preliminari risulteranno utili quando si riuniranno questi tre file in un servizio API REST.The earlier preparation pays off as you bring these three files together in a REST API service.

Per questa procedura dettagliata, usare MongoDB per archiviare le attività, come indicato in precedenza.For this walk-through, use MongoDB to store your tasks, as discussed earlier.

Nel file config.js , il database è stato denominato tasklist.In the config.js file, you called your database tasklist. Questo nome è stato anche inserito alla fine dell'URL di connessione mongoose_auth_local .That name was also what you put at the end of the 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 verrà creato automaticamente alla prima esecuzione dell'applicazione server.It creates the database for you on the first run of your server application.

Dopo aver indicato al server quale database MongoDB usare, è necessario scrivere del codice aggiuntivo per creare il modello e lo schema per le attività del server.After you tell the server which MongoDB database to use, you need to write some additional code to create the model and schema for your server tasks.

Espandere il modelloExpand the model

Questo modello di schema è sempliceThis schema model is simple. ed è possibile espanderlo in base alle esigenze.You can expand it as required.

owner: utente assegnato all'attività.owner: Who is assigned to the task. Questo oggetto è di tipo stringa.This object is a string.

Text: l'attività stessa.Text: The task itself. Questo oggetto è di tipo stringa.This object is a string.

date: data di scadenza dell'attività.date: The date that the task is due. Questo oggetto è di tipo datetime.This object is a datetime.

completed: indica se l'attività è stata completata.completed: If the task is complete. Questo oggetto è di tipo booleano.This object is a Boolean.

Creare lo schema nel codiceCreate the schema in the code

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Aprire il file server.js in un editor.Open the server.js file in an editor. Aggiungere le informazioni seguenti sotto la voce di configurazione:Add the following information below the configuration entry:

// MongoDB setup
// Setup some configuration
var serverPort = process.env.PORT || 3000; // Note we are hosting our API on port 3000
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');

// Here we create a schema to store our tasks and users. Pretty simple schema for now.
var TaskSchema = new Schema({
    owner: String,
    Text: String,
    completed: Boolean,
    date: Date
});

// Use the schema to register a model
mongoose.model('Task', TaskSchema);
var Task = mongoose.model('Task');

Creare prima lo schema e quindi creare un oggetto modello da usare per l'archiviazione dei dati nel codice quando si definiscono le route.You first create the schema, and then you create a model object that you use to store your data throughout the code when you define your routes.

Aggiungere le route per il server delle attività dell'API RESTAdd routes for your REST API task server

Ora che è disponibile un modello di database, aggiungere le route da usare per il server API REST.Now that you have a database model to work with, add the routes you use for your REST API server.

Informazioni sulle route in RestifyAbout routes in Restify

Il funzionamento delle route in Restify è identico al funzionamento nello stack di Express.Routes work in Restify in the same way that they work when they 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.

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.

Restify ed Express offrono funzionalità molto più avanzate, ad esempio la definizione di tipi di applicazione e l'esecuzione di un routing complesso tra endpoint diversi.Restify and Express can provide much deeper functionality, such as defining application types and doing complex routing across different endpoints. Ai fini di questa esercitazione verranno usate route semplici.For the purposes of this tutorial, we keep these routes simple.

Aggiungere le route predefinite al serverAdd default routes to your server

Verranno ora aggiunte le route CRUD di base create e list per l'API REST.You now add the basic CRUD routes of create and list for our REST API. Sono disponibili altre route nel ramo complete dell'esempio.Other routes can be found in the complete branch of the sample.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Aprire il file server.js in un editor.Open the server.js file in an editor. Aggiungere le informazioni seguenti sotto le voci di database create in precedenza:Below the database entries you made above add the following information:

/**
 *
 * APIs for our REST Task server
 */

// Create a task

function createTask(req, res, next) {

    // Resitify currently has a bug which doesn't allow you to set default headers
    // This headers comply with CORS and allow us to mongodbServer our 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 up and save it to Mongodb
    var _task = new Task();

    if (!req.params.Text) {
        req.log.warn({
            params: req.params
        }, 'createTodo: missing task');
        next(new MissingTaskError());
        return;
    }

    _task.owner = owner;
    _task.Text = req.params.Text;
    _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();

}
/// Simple returns the list of TODOs that were loaded.

function listTasks(req, res, next) {
    // Resitify currently has a bug which doesn't allow you to set default headers
    // This headers comply with CORS and allow us to mongodbServer 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 is 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 eventuali problemi riscontrati in un modo comprensibile per il client stesso.Add some error handling so that you can communicate any problems you encounter back to the client in a way that it can understand.

Aggiungere il codice seguente:Add the following code:

///--- Errors for communicating something interesting 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);

Creare il serverCreate your server

Dopo aver definito il database e inserito le route,You have now defined your database and put your routes in place. resta solo da aggiungere l'istanza del server che gestirà le chiamate.The last thing for you to do is to add the server instance that manages your calls.

Restify ed Express offrono un livello elevato di personalizzazione per un server API REST, ma in questo caso si userà la configurazione più semplice.Restify and Express provide deep customization for a REST API server, but we use the most basic setup here.


/**
 * Our Server
 */


var server = restify.createServer({
    name: "Microsoft Azure Active Directroy TODO Server",
    version: "2.0.1"
});

// Ensure we don't drop data on uploads
server.pre(restify.pre.pause());

// Clean up sloppy paths like //todo//////1//
server.pre(restify.pre.sanitizePath());

// Handles 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, and burst to 10
server.use(restify.throttle({
    burst: 10,
    rate: 5,
    ip: true,
}));

// Use the common stuff you probably want
server.use(restify.acceptParser(server.acceptable));
server.use(restify.dateParser());
server.use(restify.queryParser());
server.use(restify.gzipResponse());
server.use(restify.bodyParser({
    mapParams: true
})); // Allows for JSON mapping to REST
server.use(restify.authorizationParser()); // Looks for authorization headers

// Let's start using Passport.js

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

Aggiungere le route al server (senza autenticazione)Add the routes to the server (without authentication)

server.get('/api/tasks', passport.authenticate('oauth-bearer', {
    session: false
}), listTasks);
server.get('/api/tasks', passport.authenticate('oauth-bearer', {
    session: false
}), listTasks);
server.get('/api/tasks/:owner', passport.authenticate('oauth-bearer', {
    session: false
}), getTask);
server.head('/api/tasks/:owner', passport.authenticate('oauth-bearer', {
    session: false
}), getTask);
server.post('/api/tasks/:owner/:task', passport.authenticate('oauth-bearer', {
    session: false
}), createTask);
server.post('/api/tasks', passport.authenticate('oauth-bearer', {
    session: false
}), createTask);
server.del('/api/tasks/:owner/:task', passport.authenticate('oauth-bearer', {
    session: false
}), removeTask);
server.del('/api/tasks/:owner', passport.authenticate('oauth-bearer', {
    session: false
}), removeTask);
server.del('/api/tasks', passport.authenticate('oauth-bearer', {
    session: false
}), removeTask);
server.del('/api/tasks', passport.authenticate('oauth-bearer', {
    session: false
}), 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    /api/tasks/:owner/:task',
        'POST    /api/tasks (for JSON body)',
        'GET     /api/tasks',
        'PUT     /api/tasks/:owner',
        'GET     /api/tasks/:owner',
        'DELETE  /api/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/api/tasks\n';
    consoleMessage += '+++++++++++++++++++++++++++++++++++++++++++++++++++++ \n';
    consoleMessage += '\n !!! why not try a $curl -isS %s | json to get some ideas? \n';
    consoleMessage += '+++++++++++++++++++++++++++++++++++++++++++++++++++++ \n\n';

    //log.info(consoleMessage, server.name, server.url, server.url, server.url);

});

Aggiungere l'autenticazione al server API RESTAdd authentication to your REST API server

A questo punto, il server API REST in esecuzione può essere usato in Azure AD.Now that you have a running REST API server, you can make it useful against Azure AD.

Dalla riga di comando passare alla directory azuread, se non è già la posizione corrente:From the command line, change your directory to azuread, if it's not already there:

cd azuread

Usare l'oggetto OIDCBearerStrategy incluso in passport-azure-adUse the OIDCBearerStrategy that is included with passport-azure-ad

Suggerimento

Durante la scrittura delle API è sempre necessario collegare i dati a un elemento univoco dal token in modo che l'utente non possa eseguire lo spoofing.When you write APIs, you should always link the data to something unique from the token that the user can’t spoof. Quando archivia gli elementi ToDo, il server si basa sull' OID dell'utente nel token, chiamato tramite token.oid, da inserire nel campo "owner".When the server stores ToDo items, it does so based on the oid of the user in the token (called through token.oid), which goes in the “owner” field. Questo valore assicura che solo tale utente possa accedere i propri elementi ToDo.This value ensures that only that user can access their own ToDo items. Il valore di "owner" non viene esposto nell'API, così che un utente esterno può richiedere elementi TODO di altri anche se sono autenticati.There is no exposure in the API of “owner,” so an external user can request others’ ToDo items even if they are authenticated.

Usare quindi la strategia di connessione fornita con passport-azure-ad.Next, use the bearer strategy that comes with passport-azure-ad.

var findById = function(id, fn) {
    for (var i = 0, len = users.length; i < len; i++) {
        var user = users[i];
        if (user.oid === 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 retreived');
        findById(token.sub, function(err, user) {
            if (err) {
                return done(err);
            }
            if (!user) {
                // "Auto-registration"
                log.info('User was added automatically as they were new. Their sub is: ', token.oid);
                users.push(token);
                owner = token.oid;
                return done(null, token);
            }
            owner = token.sub;
            return done(null, user, token);
        });
    }
);

passport.use(oidcStrategy);

Passport usa lo stesso modello per tutte le strategie.Passport uses the same pattern for all its strategies. Viene passato un oggetto function() con token e done come parametri.You pass it a function() that has token and done as parameters. La strategia risponde dopo avere eseguito tutte le relative operazioni.The strategy comes back to you after it has done all of its work. Archiviare quindi l'utente e salvare il token per non doverlo richiedere nuovamente.You should then store the user and save the token so that you don’t need to ask for it again.

Importante

Il codice precedente accetta qualsiasi utente che esegue l'autenticazione al server.The code above takes any user who happens to authenticate to your server. Questa operazione è nota come registrazione automatica.This process is known as autoregistration. Nei server di produzione non consentire l'accesso degli utenti all'API senza prima un processo di registrazione.In production servers, don't let in any users access the API without first having them go through a registration process. Questo processo è il modello in genere adottato per le app consumer che consentono di eseguire la registrazione usando Facebook, ma che chiedono di immettere informazioni aggiuntive.This process is usually the pattern you see in consumer apps that allow you to register by using Facebook but then ask you to fill out additional information. Se non si trattasse di un programma della riga di comando, sarebbe possibile estrarre il messaggio di posta elettronica dall'oggetto token restituito e chiedere agli utenti di immettere informazioni aggiuntive.If this program wasn’t a command-line program, we could have extracted the email from the token object that is returned and then asked users to fill out additional information. Trattandosi di un esempio, le informazioni verranno aggiunte a un database in memoria.Because this is a sample, we add them to an in-memory database.

Eseguire l'applicazione server per verificare che rifiuti l'utenteRun your server application to verify that it rejects you

Per verificare se la protezione OAuth2 per gli endpoint è attiva, usare curl .You can use curl to see if you now have OAuth2 protection against your endpoints. Le intestazioni restituite dovrebbero essere sufficienti a capire se si sta procedendo nel modo corretto.The headers returned should be enough to tell you that you are on the right path.

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

$sudo mongodb

Passare alla directory ed eseguire il server:Change to the directory and run the server:

$ cd azuread
$ node server.js

In una nuova finestra del terminale eseguire curlIn a new terminal window, run curl

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

$ curl -isS -X POST http://127.0.0.1:3000/api/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

L'errore 401 è la risposta prevista.A 401 error is the response you want. Indica che il livello Passport sta provando a eseguire il reindirizzamento all'endpoint di autorizzazione.It indicates that the Passport layer is trying to redirect to the authorize endpoint.

Ora è disponibile un servizio API REST che usa OAuth2You now have a REST API service that uses OAuth2

È stata implementata un'API REST usando Restify e OAuth.You have implemented a REST API by using Restify and OAuth! Il codice ora disponibile consente di continuare a sviluppare il servizio sulla base di questo esempio.You now have sufficient code so that you can continue to develop your service and build on this example. Sono state eseguite tutte le operazioni possibili con questo server senza usare un client compatibile con OAuth2.You have gone as far as you can with this server without using an OAuth2-compatible client. Per il passaggio successivo usare una procedura dettagliata aggiuntiva come Azure AD B2C: chiamare un'API Web da un'applicazione iOS con una libreria di terze parti .For that next step use an additional walk-through like our Connect to a web API by using iOS with B2C walkthrough.

Passaggi successiviNext steps

Ora è possibile passare ad argomenti più avanzati, ad esempio:You can now move to more advanced topics, such as:

Connettersi a un'API Web usando iOS con B2C >>Connect to a web API by using iOS with B2C