Azure AD B2C: aggiungere l'accesso a un'app Web Node.jsAzure AD B2C: Add sign-in to a Node.js web app

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.

Per Azure Active Directory (Azure AD) è possibile installare questo modulo e aggiungere il plug-in passport-azure-ad di Azure Active Directory.For Azure Active Directory (Azure AD), can install this module and then add the Azure AD passport-azure-ad plug-in.

È necessario:You need to:

  1. Registrare un'applicazione usando Azure AD.Register an application by using Azure AD.
  2. Configurare l'app per usare il plug-in passport-azure-ad.Set up your app to use the passport-azure-ad plug-in.
  3. Usare Passport per inviare le richieste di accesso e disconnessione ad Azure AD.Use Passport to issue sign-in and sign-out requests to Azure AD.
  4. Stampare i dati utente.Print user data.

Il codice per questa esercitazione è disponibile in GitHub.The code for this tutorial is maintained on GitHub. Per seguire la procedura, è possibile scaricare la struttura dell'app come file ZIP.To follow along, you can download the app's skeleton as a .zip file. È anche possibile clonare la struttura:You can also clone the skeleton:

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

Al termine dell'esercitazione, verrà fornita l'applicazione completata.The completed application is provided at the end of this tutorial.

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 così via.A directory is a container for all of your users, apps, groups, and more. Se non è già stato fatto, creare una directory B2C prima di proseguire con questa guida.If you don't have one already, create a B2C directory before you continue in this guide.

Creare un'applicazioneCreate an application

Successivamente, è necessario creare un'app nella directory B2C.Next, you need to create an app in your B2C directory. In questo modo Azure AD acquisisce le informazioni necessarie per comunicare in modo sicuro con l'app.This gives Azure AD information that it needs to communicate securely with your app. Sia l'app client che l'API Web saranno rappresentate da un unico ID applicazione, perché includono un'app per la logica.Both the client app and web API will be 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'applicazione.Include a web app/web API in the application.
  • Immettere http://localhost:3000/auth/openid/return come URL di risposta.Enter http://localhost:3000/auth/openid/return 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. Sarà necessario più avanti.You will need it 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. Sarà necessario più avanti.You'll also need this later.

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 tre esperienze di identità: iscrizione, accesso e accesso con Facebook.This app contains three identity experiences: sign up, sign in, and sign in by using Facebook. È necessario creare i criteri per ogni tipo, come descritto nell' articolo di riferimento per i criteri.You need to create this 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 ogni criterio dopo averlo creato.Copy 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'll need these 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 create your three policies, you're ready to build your app.

Si noti che questo articolo non illustra come usare i criteri appena creati.Note that this article does not cover how to use the policies you just created. 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.

Aggiungere prerequisiti alla directoryAdd prerequisites to your directory

Dalla riga di comando passare alla cartella radice, se necessario.From the command line, change directories to your root folder, if you're not already there. Eseguire i comandi seguenti:Run the following commands:

  • npm install express
  • npm install ejs
  • npm install ejs-locals
  • npm install restify
  • npm install mongoose
  • npm install bunyan
  • npm install assert-plus
  • npm install passport
  • npm install webfinger
  • npm install body-parser
  • npm install express-session
  • npm install cookie-parser

È stato anche usato passport-azure-ad per l'anteprima nella struttura di avvio rapido.In addition, we used passport-azure-ad for our preview in the skeleton of the Quickstart.

  • npm install passport-azure-ad

Verranno installate le librerie da cui dipende passport-azure-ad.This will install the libraries that passport-azure-ad depends on.

Configurare l'app per l'uso della strategia Passport-Node.jsSet up your app to use the Passport-Node.js strategy

Configurare il middleware Express per l'uso del protocollo di autenticazione OpenID Connect.Configure the Express middleware to use the OpenID Connect authentication protocol. Passport verrà usato, tra le altre azioni, per inviare le richieste di accesso e disconnessione, gestire le sessioni degli utenti e ottenere informazioni sugli utenti.Passport will be used to issue sign-in and sign-out requests, manage user sessions, and get information about users, among other actions.

Aprire il file config.js nella radice del progetto e immettere i valori di configurazione dell'app nella sezione exports.creds.Open the config.js file in the root of the project and enter your app's configuration values in the exports.creds section.

  • clientID: ID applicazione assegnato all'app nel portale di registrazione.clientID: The Application ID assigned to your app in the registration portal.
  • returnURL: URI di reindirizzamento immesso nel portale.returnURL: The Redirect URI you entered in the portal.
  • tenantName: nome del tenant dell'app, ad esempio, contoso.onmicrosoft.com.tenantName: The tenant name of your app, for example, contoso.onmicrosoft.com.

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.

Aprire il file app.js nella radice del progetto.Open the app.js file in the root of the project. Aggiungere la chiamata seguente per richiamare la strategia OIDCStrategy fornita con passport-azure-ad.Add the following call to invoke the OIDCStrategy strategy that comes with passport-azure-ad.

var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;

// Add some logging
var log = bunyan.createLogger({
    name: 'Microsoft OIDC Example Web Application'
});

Usare la strategia a cui si è appena fatto riferimento per gestire le richieste di accesso.Use the strategy you just referenced to handle sign-in requests.

// Use the OIDCStrategy in Passport (Section 2).
//
//   Strategies in Passport require a "validate" function that accepts
//   credentials (in this case, an OpenID identifier), and invokes a callback
//   by using a user object.
passport.use(new OIDCStrategy({
    callbackURL: config.creds.returnURL,
    realm: config.creds.realm,
    clientID: config.creds.clientID,
    oidcIssuer: config.creds.issuer,
    identityMetadata: config.creds.identityMetadata,
    skipUserProfile: config.creds.skipUserProfile,
    responseType: config.creds.responseType,
    responseMode: config.creds.responseMode,
    tenantName: config.creds.tenantName
  },
  function(iss, sub, profile, accessToken, refreshToken, done) {
    log.info('Example: Email address we received was: ', profile.email);
    // asynchronous verification, for effect...
    process.nextTick(function () {
      findByEmail(profile.email, function(err, user) {
        if (err) {
          return done(err);
        }
        if (!user) {
          // "Auto-registration"
          users.push(profile);
          return done(null, profile);
        }
        return done(null, user);
      });
    });
  }
));

Passport usa un modello simile per tutte le strategie (inclusi Twitter e Facebook).Passport uses a similar pattern for all of its strategies (including Twitter and Facebook). Tutti i writer di strategie rispettano questo modello.All strategy writers adhere to this pattern. Quando si esamina la strategia, è possibile osservare che le si passa un oggetto function() che ha come parametri un token e un oggetto done.When you look at the strategy, you can see that you pass it a function() that has a token and a done as the 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 l'utente e accantonare il token per non doverlo richiedere nuovamente.Store the user and stash the token so that you don’t need to ask for it again.

Importante

Il codice precedente accetta tutti gli utenti autenticati dal server.The preceding code takes all users whom the server authenticates. Questa operazione è la registrazione automatica.This is autoregistration. Quando si usano server di produzione, si consente l'accesso solo agli utenti che abbiano eseguito un processo di registrazione configurato.When you use production servers, you don’t want to let in users unless they have gone through a registration process that you have set up. Questo modello è frequente nelle app consumerYou can often see this pattern in consumer apps. che consentono di eseguire la registrazione usando Facebook, ma che chiedono anche di inserire altre informazioni.These allow you to register by using Facebook, but then they ask you to fill out additional information. Se non si trattasse di un'applicazione di esempio, si estrarrebbe un indirizzo di posta elettronica dall'oggetto token restituito e si chiederebbe all'utente di immettere informazioni aggiuntive.If our application wasn’t a sample, we could extract an email address from the token object that is returned, and then ask the user to fill out additional information. Trattandosi di un server di test, è sufficiente aggiungere gli utenti al database in memoria.Because this is a test server, we simply add users to the in-memory database.

Aggiungere i metodi che consentono di tenere traccia degli utenti che hanno eseguito l'accesso, come richiesto da Passport.Add the methods that allow you to keep track of users who have signed in, as required by Passport. Questa operazione include la serializzazione e la deserializzazione delle informazioni dell'utente:This includes serializing and deserializing user information:


// Passport session setup. (Section 2)

//   To support persistent sign-in sessions, Passport needs to be able to
//   serialize users into and deserialize users out of sessions. Typically,
//   this is as simple as storing the user ID when Passport serializes a user
//   and finding the user by ID when Passport deserializes that user.
passport.serializeUser(function(user, done) {
  done(null, user.email);
});

passport.deserializeUser(function(id, done) {
  findByEmail(id, function (err, user) {
    done(err, user);
  });
});

// Array to hold users who have signed in
var users = [];

var findByEmail = function(email, fn) {
  for (var i = 0, len = users.length; i < len; i++) {
    var user = users[i];
    log.info('we are using user: ', user);
    if (user.email === email) {
      return fn(null, user);
    }
  }
  return fn(null, null);
};

Aggiungere il codice per caricare il motore di Express.Add the code to load the Express engine. Nel codice seguente è possibile notare che vengono usati i modelli /views e /routes predefiniti forniti da Express.In the following, you can see that we use the default /views and /routes pattern that Express provides.


// configure Express (Section 2)

var app = express();


app.configure(function() {
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.logger());
  app.use(express.methodOverride());
  app.use(cookieParser());
  app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
  app.use(bodyParser.urlencoded({ extended : true }));
  // Initialize Passport!  Also use passport.session() middleware to support
  // persistent sign-in sessions (recommended).
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(app.router);
  app.use(express.static(__dirname + '/../../public'));
});

Aggiungere le route POST che trasferiscono le richieste di accesso effettive al motore passport-azure-ad:Add the POST routes that hand off the actual sign-in requests to the passport-azure-ad engine:


// Our Auth routes (Section 3)

// GET /auth/openid
//   Use passport.authenticate() as route middleware to authenticate the
//   request. The first step in OpenID authentication involves redirecting
//   the user to an OpenID provider. After the user is authenticated,
//   the OpenID provider redirects the user back to this application at
//   /auth/openid/return

app.get('/auth/openid',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
  function(req, res) {
    log.info('Authentication was called in the Sample');
    res.redirect('/');
  });

// GET /auth/openid/return
//   Use passport.authenticate() as route middleware to authenticate the
//   request. If authentication fails, the user will be redirected back to the
//   sign-in page. Otherwise, the primary route function will be called.
//   In this example, it redirects the user to the home page.
app.get('/auth/openid/return',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
  function(req, res) {

    res.redirect('/');
  });

// POST /auth/openid/return
//   Use passport.authenticate() as route middleware to authenticate the
//   request. If authentication fails, the user will be redirected back to the
//   sign-in page. Otherwise, the primary route function will be called.
//   In this example, it will redirect the user to the home page.

app.post('/auth/openid/return',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
  function(req, res) {

    res.redirect('/');
  });

Usare Passport per inviare le richieste di accesso e disconnessione ad Azure ADUse Passport to issue sign-in and sign-out requests to Azure AD

L'app ora è configurata correttamente per comunicare con l'endpoint 2.0 mediante il protocollo di autenticazione OpenID Connect.Your app is now properly configured to communicate with the v2.0 endpoint by using the OpenID Connect authentication protocol. passport-azure-ad ha gestito i dettagli relativi alla creazione dei messaggi di autenticazione, alla convalida dei token da Azure AD e alla gestione della sessione utente.passport-azure-ad has taken care of the details of crafting authentication messages, validating tokens from Azure AD, and maintaining user session. A questo punto, è sufficiente offrire agli utenti un modo per accedere e disconnettersi e per raccogliere informazioni aggiuntive sugli utenti connessi.All that remains is to give your users a way to sign in and sign out, and to gather additional information on users who have signed in.

Aggiungere prima di tutto i metodi predefinito, di accesso, account e disconnessione al file app.js:First, add the default, sign-in, account, and sign-out methods to your app.js file:


//Routes (Section 4)

app.get('/', function(req, res){
  res.render('index', { user: req.user });
});

app.get('/account', ensureAuthenticated, function(req, res){
  res.render('account', { user: req.user });
});

app.get('/login',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/login' }),
  function(req, res) {
    log.info('Login was called in the Sample');
    res.redirect('/');
});

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

Per esaminare questi metodi in dettaglio:To review these methods in detail:

  • La route / viene reindirizzata alla visualizzazione index.ejs passando l'utente nella richiesta (se presente).The / route redirects to the index.ejs view by passing the user in the request (if it exists).
  • La route /account verifica prima di tutto che l'utente sia autenticato. L'implementazione per questo passaggio è disponibile più avanti.The /account route first verifies that you are authenticated (the implementation for this is below). quindi passa l'utente nella richiesta per poter ottenere altre informazioni sull'utente.It then passes the user in the request so that you can get additional information about the user.
  • La route /login chiama l'autenticatore azuread-openidconnect da passport-azure-ad.The /login route calls the azuread-openidconnect authenticator from passport-azure-ad. Se l'esito è negativo, la route reindirizza di nuovo l'utente a /login.If it doesn't succeed, the route redirects the user back to /login.
  • /logout chiama semplicemente logout.ejs e la rispettiva route./logout simply calls logout.ejs (and its route). I cookie vengono cancellati e quindi l'utente torna a index.ejs.This clears cookies and then returns the user back to index.ejs.

Per l'ultima parte di app.js, aggiungere il metodo EnsureAuthenticated usato nella route /account.For the last part of app.js, add the EnsureAuthenticated method that is used in the /account route.


// Simple route middleware to ensure that the user is authenticated. (Section 4)

//   Use this route middleware on any resource that needs to be protected. If
//   the request is authenticated (typically via a persistent sign-in session),
//   then the request will proceed. Otherwise, the user will be redirected to the
//   sign-in page.
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) { return next(); }
  res.redirect('/login')
}

Creare infine il server stesso in app.js.Finally, create the server itself in app.js.


app.listen(3000);

Creare le visualizzazioni e le route in Express per chiamare i criteriCreate the views and routes in Express to call your policies

app.js è stato completato.Your app.js is now complete. È sufficiente aggiungere le route e le visualizzazioni che consentono di chiamare i criteri di accesso e di iscrizione.You just need to add the routes and views that allow you to call the sign-in and sign-up policies. In questo modo vengono gestite anche le route /logout e /login create.These also handle the /logout and /login routes you created.

Creare la route /routes/index.js nella directory radice.Create the /routes/index.js route under the root directory.


/*
 * GET home page.
 */

exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

Creare la route /routes/user.js nella directory radice.Create the /routes/user.js route under the root directory.


/*
 * GET users listing.
 */

exports.list = function(req, res){
  res.send("respond with a resource");
};

Queste semplici route passano le richieste alle visualizzazioni.These simple routes pass along requests to your views. Includono l'utente, se è presente.They include the user, if one is present.

Creare la vista /views/index.ejs nella directory radice.Create the /views/index.ejs view under the root directory. Si tratta di una semplice pagina che chiama i criteri per l'accesso e la disconnessione. È possibile usarla anche per ottenere informazioni account.This is a simple page that calls policies for sign-in and sign-out. You can also use it to grab account information. Si noti che è possibile usare if (!user) condizionale quando l'utente viene passato nella richiesta per fornire la prova che l'utente ha eseguito l'accesso.Note that you can use the conditional if (!user) as the user is passed through in the request to provide evidence that the user is signed in.

<% if (!user) { %>
    <h2>Welcome! Please sign in.</h2>
    <a href="/login/?p=your facebook policy">Sign in with Facebook</a>
    <a href="/login/?p=your email sign-in policy">Sign in with email</a>
    <a href="/login/?p=your email sign-up policy">Sign up with email</a>
<% } else { %>
    <h2>Hello, <%= user.displayName %>.</h2>
    <a href="/account">Account info</a></br>
    <a href="/logout">Log out</a>
<% } %>

Creare la visualizzazione /views/account.ejs nella directory radice per poter visualizzare informazioni aggiuntive che passport-azure-ad ha inserito nella richiesta dell'utente.Create the /views/account.ejs view under the root directory so that you can view additional information that passport-azure-ad put in the user request.

<% if (!user) { %>
    <h2>Welcome! Please sign in.</h2>
    <a href="/login">Sign in</a>
<% } else { %>
<p>displayName: <%= user.displayName %></p>
<p>givenName: <%= user.name.givenName %></p>
<p>familyName: <%= user.name.familyName %></p>
<p>UPN: <%= user._json.upn %></p>
<p>Profile ID: <%= user.id %></p>
<p>Full Claims</p>
<%- JSON.stringify(user) %>
<p></p>
<a href="/logout">Log Out</a>
<% } %>

Ora è possibile compilare ed eseguire l'app.You can now build and run your app.

Eseguire node app.js e passare a http://localhost:3000.Run node app.js and navigate to http://localhost:3000

Iscriversi o accedere all'app usando la posta elettronica o Facebook.Sign up or sign in to the app by using email or Facebook. Disconnettersi e accedere nuovamente come un utente diverso.Sign out and sign back in as a different user.

Passaggi successiviNext steps

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

git clone --branch complete https://github.com/AzureADQuickStarts/B2C-WebApp-OpenIDConnect-nodejs.git

Ora è possibile passare ad argomenti più avanzati.You can now move on to more advanced topics. È possibile provare a:You might try:

Proteggere un'API Web usando il modello B2C in Node.jsSecure a web API by using the B2C model in Node.js