Azure AD B2C: adicionar entrada a um aplicativo Web do Node.jsAzure AD B2C: Add sign-in to a Node.js web app

Passport é middleware de autenticação para o Node.js.Passport is authentication middleware for Node.js. Extremamente flexível e modular, o Passport pode ser instalado sem impedimento em qualquer aplicativo Web baseado em Express ou Restify.Extremely flexible and modular, Passport can be unobtrusively installed in any Express-based or Restify web application. Um conjunto abrangente de estratégias que dão suporte à autenticação usando um nome de usuário e uma senha, o Facebook, o Twitter e muito mais.A comprehensive set of strategies supports authentication by using a user name and password, Facebook, Twitter, and more.

Desenvolvemos uma estratégia para o Azure AD (Active Directory do Azure).We have developed a strategy for Azure Active Directory (Azure AD). Você instalará esse módulo e, em seguida, adicionará o plug-in passport-azure-ad do Azure AD.You will install this module and then add the Azure AD passport-azure-ad plug-in.

Para fazer isso, você precisa:To do this, you need to:

  1. Registre um aplicativo usando o Azure AD.Register an application by using Azure AD.
  2. Configure seu aplicativo para usar o plug-in passport-azure-ad.Set up your app to use the passport-azure-ad plug-in.
  3. Usar o Passport para emitir solicitações de entrada e saída ao AD do Azure.Use Passport to issue sign-in and sign-out requests to Azure AD.
  4. Imprima dados de usuário.Print user data.

O código para este tutorial é mantido no GitHub.The code for this tutorial is maintained on GitHub. Para acompanhá-lo, você pode baixar o esqueleto do aplicativo como um arquivo .zip.To follow along, you can download the app's skeleton as a .zip file. Também é possível clonar o esqueleto:You can also clone the skeleton:

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

O aplicativo completo é fornecido no fim deste tutorial.The completed application is provided at the end of this tutorial.

Obter um diretório AD B2C do AzureGet an Azure AD B2C directory

Antes de usar AD B2C do Azure, você deve criar um diretório ou locatário.Before you can use Azure AD B2C, you must create a directory, or tenant. Um diretório é um contêiner para todos os seus usuários, aplicativos, grupos etc.A directory is a container for all of your users, apps, groups, and more. Se você ainda não tiver um, crie um diretório B2C antes de prosseguir neste guia.If you don't have one already, create a B2C directory before you continue in this guide.

Criar um aplicativoCreate an application

Em seguida, você precisa criar um aplicativo em seu diretório B2C.Next, you need to create an app in your B2C directory. Isso fornece ao AD do Azure as informações de que ele precisa para se comunicar de forma segura com seu aplicativo.This gives Azure AD information that it needs to communicate securely with your app. O aplicativo cliente e a API Web serão representados por uma única ID do Aplicativo, pois eles abrangem um aplicativo lógico.Both the client app and web API will be represented by a single Application ID, because they comprise one logical app. Para criar um aplicativo, siga estas instruções.To create an app, follow these instructions. É necessário que você:Be sure to:

  • Incluir um aplicativo Web/api Web no aplicativo.Include a web app/web API in the application.
  • Digite http://localhost:3000/auth/openid/return como uma URL de Resposta.Enter http://localhost:3000/auth/openid/return as a Reply URL. É a URL padrão deste exemplo de código.It is the default URL for this code sample.
  • Crie um Segredo de aplicativo para seu aplicativo e copie-o.Create an Application secret for your application and copy it. Você precisará dela mais tarde.You will need it later. Observe que esse valor precisa ser seguido por caracteres de escape XML antes de ser usado.Note that this value needs to be XML escaped before you use it.
  • Copie a ID de aplicativo atribuída ao aplicativo.Copy the Application ID that is assigned to your app. Você também precisará dela mais tarde.You'll also need this later.

Importante

Não é possível usar aplicativos registrados na guia Aplicativos no Portal de Gerenciamento do Azure para fazer isso.You cannot use applications registered in the Applications tab on the classic Azure Management Portal for this.

Criar suas políticasCreate your policies

No Azure AD B2C, toda experiência do usuário é definida por uma política.In Azure AD B2C, every user experience is defined by a policy. Esse aplicativo contém três experiências de identidade: inscrição, entrada e entrada usando o Facebook.This app contains three identity experiences: sign up, sign in, and sign in by using Facebook. Você precisa criar esta política de cada tipo, conforme descrito no artigo de referência de política.You need to create this policy of each type, as described in the policy reference article. Ao criar as três políticas, não deixe de:When you create your three policies, be sure to:

  • Escolher o Nome de exibição e outros atributos de inscrição em sua política de inscrição.Choose the Display name and other sign-up attributes in your sign-up policy.
  • Escolha as declarações de aplicativo Nome de exibição e ID do Objeto em todas as políticas.Choose the Display name and Object ID application claims in every policy. Você pode escolher outras declarações também.You can choose other claims as well.
  • Copie o Nome de cada política depois de criá-la.Copy the Name of each policy after you create it. Ele deve ter o prefixo b2c_1_.It should have the prefix b2c_1_. Você precisará esses nomes de política mais tarde.You'll need these policy names later.

Observação

No AD B2C do Azure, o nome da política será prefixado com b2c_1_, como b2c_1_sign_up.In Azure AD B2C, your policy's name will be prefixed with b2c_1_, like b2c_1_sign_up. Você é livre para usar as políticas em todos os seus aplicativos cliente e servidor.You are free to use your policies across all of your apps, both client and server. Se você criou anteriormente políticas em outro passo a passo do B2C, não é necessário fazê-lo novamente.If you've previously created policies in another B2C walk-through, there is no need to do so again. Você pode reutilizar as políticas que criou anteriormente no portal se elas correspondem aos requisitos do aplicativo.You may reuse the policies you've previously created in the portal if they match the requirements of the application.

Depois de criar as três políticas, você estará pronto para compilar o aplicativo.After you create your three policies, you're ready to build your app.

Observe que este artigo não aborda como usar as políticas que você acabou de criar.Note that this article does not cover how to use the policies you just created. Para saber mais sobre o funcionamento das políticas no Azure AD B2C, comece com o tutorial de introdução ao aplicativo Web do .NET.To learn about how policies work in Azure AD B2C, start with the .NET web app getting started tutorial.

Adicionar pré-requisitos a seu diretórioAdd prerequisites to your directory

Na linha de comando, altere os diretórios para a pasta raiz, se você não ainda estiver lá.From the command line, change directories to your root folder, if you're not already there. Execute os seguintes comandos: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

Além disso, usamos passport-azure-ad para a visualização no esqueleto do Início Rápido.In addition, we used passport-azure-ad for our preview in the skeleton of the Quickstart.

  • npm install passport-azure-ad

Isso instalará as bibliotecas das quais o passport-azure-ad depende.This will install the libraries that passport-azure-ad depends on.

Configurar seu aplicativo para usar a estratégia do Passport-Node.jsSet up your app to use the Passport-Node.js strategy

Configure o middleware Express para usar o protocolo de autenticação OpenID Connect.Configure the Express middleware to use the OpenID Connect authentication protocol. O Passport será usado para emitir solicitações de entrada e saída, gerenciar sessões de usuário e obter informações sobre usuários, entre outras ações.Passport will be used to issue sign-in and sign-out requests, manage user sessions, and get information about users, among other actions.

Abra o arquivo config.js na raiz do projeto e insira os valores de configuração do aplicativo na seção 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: a ID do Aplicativo atribuída a seu aplicativo no portal de registro.clientID: The Application ID assigned to your app in the registration portal.
  • returnURL: o URI de Redirecionamento que você inseriu no portal.returnURL: The Redirect URI you entered in the portal.
  • tenantName: o nome do locatário de seu aplicativo; por exemplo, contoso.onmicrosoft.com.tenantName: The tenant name of your app, for example, contoso.onmicrosoft.com.

Observação

O nome do locatário do B2C é o domínio que você inseriu durante a criação de locatário e é exibido na folha do diretório no portal do 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. Ele geralmente termina com o sufixo .onmicrosoft.com, por exemplo, contosob2c.onmicrosoft.com.It usually ends with the suffix .onmicrosoft.com, for instance, contosob2c.onmicrosoft.com.

Abra o arquivo app.js na raiz do projeto.Open the app.js file in the root of the project. Adicione a chamada a seguir para invocar a estratégia OIDCStrategy que vem com o 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'
});

Use a estratégia referenciada para manipular solicitações de entrada.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);
      });
    });
  }
));

O Passport usa um padrão semelhante para todas as suas estratégias (incluindo o Twitter e o Facebook).Passport uses a similar pattern for all of its strategies (including Twitter and Facebook). Todos os gravadores de estratégia seguem esse padrão.All strategy writers adhere to this pattern. Ao examinar a estratégia, você pode ver que passa para ela um function() que tem um token e um done como parâmetros.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. A estratégia volta para você após fazer seu trabalho.The strategy comes back to you after it has done all of its work. Armazene o usuário e guarde o token para que não precise pedi-lo novamente.Store the user and stash the token so that you don’t need to ask for it again.

Importante

O código anterior usa todos os usuários que o servidor autentica.The preceding code takes all users whom the server authenticates. Esse é o registro automático.This is autoregistration. Ao usar servidores de produção, não convém permitir a inclusão de usuários, a menos que eles passem por um processo de registro configurado por você.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. Frequentemente, você pode ver esse padrão em aplicativos de consumidor.You can often see this pattern in consumer apps. Eles permitem que você se registre usando o Facebook, mas, em seguida, pedem que você preencha informações adicionais.These allow you to register by using Facebook, but then they ask you to fill out additional information. Se o aplicativo não fosse um exemplo, poderíamos extrair um endereço de email do objeto de token que é retornado e, em seguida, pedir ao usuário que preenchesse informações adicionais.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. Como esse é um servidor de teste, basta adicionar usuários ao banco de dados na memória.Because this is a test server, we simply add users to the in-memory database.

Adicione os métodos que lhe permitem acompanhar os usuários que se inscreveram, conforme solicitado pelo Passport.Add the methods that allow you to keep track of users who have signed in, as required by Passport. Isso inclui a serialização e a desserialização de informações do usuário: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);
};

Adicione o código para carregar o mecanismo Express.Add the code to load the Express engine. A seguir, você pode ver que usamos o padrão /views e /routes padrão fornecidos pelo 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'));
});

Adicione as rotas POST que entregarão as solicitações de logon reais ao mecanismo 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('/');
  });

Usar o Passport para emitir solicitações de entrada e saída ao AD do AzureUse Passport to issue sign-in and sign-out requests to Azure AD

Seu aplicativo agora está configurado corretamente para se comunicar com o ponto de extremidade v2.0 usando o protocolo de autenticação OpenID Connect.Your app is now properly configured to communicate with the v2.0 endpoint by using the OpenID Connect authentication protocol. O passport-azure-ad cuidou de todos os detalhes da criação de mensagens de autenticação, validação de tokens do Azure AD e manutenção da sessão do usuário.passport-azure-ad has taken care of the details of crafting authentication messages, validating tokens from Azure AD, and maintaining user session. Tudo o que resta é fornecer aos usuários uma maneira de entrar e sair e obter informações adicionais sobre os usuários que entraram.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.

Primeiro, adicione a conta de entrada padrão e métodos de saída ao arquivo 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('/');
});

Para examinar esses métodos em detalhes:To review these methods in detail:

  • A rota / é redirecionada para o modo de exibição index.ejs passando o usuário na solicitação (se houver).The / route redirects to the index.ejs view by passing the user in the request (if it exists).
  • A rota /account verifica primeiro se você está autenticado (a implementação para isso está abaixo).The /account route first verifies that you are authenticated (the implementation for this is below). Em seguida, ela passa o usuário na solicitação para que você possa obter informações adicionais sobre o usuário.It then passes the user in the request so that you can get additional information about the user.
  • A rota /login chama o autenticador azuread-openidconnect de passport-azure-ad.The /login route calls the azuread-openidconnect authenticator from passport-azure-ad. Se não tiver êxito, a rota redirecionará o usuário de volta para /login.If it doesn't succeed, the route redirects the user back to /login.
  • /logout simplesmente chama logout.ejs (e sua rota)./logout simply calls logout.ejs (and its route). Isso limpa os cookies e, em seguida, retorna o usuário para index.ejs.This clears cookies and then returns the user back to index.ejs.

Para a última parte de app.js, adicione o método EnsureAuthenticated que é usado na rota /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')
}

Por fim, crie o próprio servidor em app.js.Finally, create the server itself in app.js.


app.listen(3000);

Crie esses modos de exibição e rotas no Express para chamar as políticasCreate the views and routes in Express to call your policies

O app.js já foi concluído.Your app.js is now complete. Basta adicionar as rotas e os modos de exibição que lhe permitem chamar as políticas de entrada e de inscrição.You just need to add the routes and views that allow you to call the sign-in and sign-up policies. Elas também lidam com as rotas /logout e /login que você criou.These also handle the /logout and /login routes you created.

Criar a rota /routes/index.js no diretório raiz.Create the /routes/index.js route under the root directory.


/*
 * GET home page.
 */

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

Criar a rota /routes/user.js no diretório raiz.Create the /routes/user.js route under the root directory.


/*
 * GET users listing.
 */

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

Essas rotas simples passam solicitações para seus modos de exibição.These simple routes pass along requests to your views. Elas incluem o usuário, caso esteja presente.They include the user, if one is present.

Crie o modo de exibição /views/index.ejs no diretório raiz.Create the /views/index.ejs view under the root directory. Essa é uma página simples que chama as políticas de entrada e saída. Você também pode usá-la para obter informações sobre a conta.This is a simple page that calls policies for sign-in and sign-out. You can also use it to grab account information. Observe que você pode usar o if (!user) condicional enquanto o usuário é passado na solicitação para comprovar que o usuário está conectado.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>
<% } %>

Crie o modo de exibição /views/account.ejs no diretório raiz para que você possa exibir informações adicionais que passport-azure-ad colocou na solicitação do usuário.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>
<% } %>

Agora você pode compilar e executar o aplicativo.You can now build and run your app.

Execute node app.js e navegue até http://localhost:3000Run node app.js and navigate to http://localhost:3000

Registre-se ou entre no aplicativo usando o email ou o Facebook.Sign up or sign in to the app by using email or Facebook. Saia e entre novamente como outro usuário.Sign out and sign back in as a different user.

Próximas etapasNext steps

Para referência, o exemplo completo (sem seus valores de configuração) é fornecido como um arquivo .zip.For reference, the completed sample (without your configuration values) is provided as a .zip file. Você também pode cloná-lo do GitHub:You can also clone it from GitHub:

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

Agora você pode passar para tópicos mais avançados.You can now move on to more advanced topics. Você pode experimentar:You might try:

Proteger uma API Web usando o modelo B2C no Node.jsSecure a web API by using the B2C model in Node.js