Habilitación de la autenticación en su propia aplicación web de Node mediante Azure Active Directory B2C

En este artículo, aprenderá a agregar la autenticación de Azure Active Directory B2C (Azure AD B2C) en su propia aplicación web de Node.js. Permitirá a los usuarios iniciar sesión, cerrar sesión, actualizar el perfil y restablecer la contraseña mediante flujos de usuario de Azure AD B2C. En este artículo se usa la Biblioteca de autenticación de Microsoft (MSAL) para Node para simplificar la adición de autenticación a la aplicación web de Node.

El objetivo de este artículo es sustituir la aplicación de ejemplo que usó en Configuración de la autenticación en una aplicación web Node.js de ejemplo mediante Azure AD B2C con su propia aplicación web Node.js web.

En este artículo se usa Node.js y Express para crear una aplicación web de Node.js básica. Las vistas de la aplicación usan Handlebars.

Requisitos previos

Paso 1: Creación del proyecto de Node

Cree una carpeta para hospedar la aplicación de Node, como active-directory-b2c-msal-node-sign-in-sign-out-webapp.

  1. En el terminal, cambie el directorio a la carpeta de la aplicación de Node, como cd active-directory-b2c-msal-node-sign-in-sign-out-webapp, y ejecute npm init -y. Este comando crea un archivo package.json predeterminado para el proyecto de Node.js.

  2. En el terminal, ejecute npm install express. Este comando instala el marco Express.

  3. Cree más carpetas y archivos para lograr esta estructura de proyecto:

    active-directory-b2c-msal-node-sign-in-sign-out-webapp/
    ├── index.js
    └── package.json
    └── .env
    └── views/
        └── layouts/
            └── main.hbs
        └── signin.hbs
    

La carpeta views contiene los archivos de Handlebars para la interfaz de usuario de la aplicación.

Paso 2: Instalación de dependencias de aplicaciones

En el terminal, ejecute los comandos siguientes para instalar los paquetes dotenv, express-handlebars, express-session y @azure/msal-node:

npm install dotenv
npm install express-handlebars
npm install express-session
npm install @azure/msal-node

Paso 3: Compilación de componentes de la interfaz de usuario de la aplicación

En el archivo main.hbs, agregue el código siguiente:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <title>Tutorial | Authenticate users with MSAL for B2C</title>

    <!-- adding Bootstrap 4 for UI components  -->
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <link rel="SHORTCUT ICON" href="https://c.s-microsoft.com/favicon.ico?v2" type="image/x-icon">
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
      <a class="navbar-brand" href="/">Microsoft Identity Platform</a>
        {{#if showSignInButton}}
            <div class="ml-auto">
                <a type="button" id="SignIn" class="btn btn-secondary" href="/signin" aria-haspopup="true" aria-expanded="false">
                    Sign in
                </a>
            </div>
        {{else}}
              <div class="ml-auto">
                  <a type="button" id="EditProfile" class="btn btn-warning" href="/profile" aria-haspopup="true" aria-expanded="false">
                      Edit profile
                  </a>
                  <a type="button" id="PasswordReset" class="btn btn-warning" href="/password" aria-haspopup="true" aria-expanded="false">
                      Reset password
                  </a>
              </div>

                <p class="navbar-brand d-flex ms-auto">Hi {{givenName}}</p>

                <a class="navbar-brand d-flex ms-auto" href="/signout">Sign out</a>
        {{/if}}
    </nav>
    <br>
    <h5 class="card-header text-center">MSAL Node Confidential Client application with Auth Code Flow</h5>
    <br>
    <div class="row" style="margin:auto" >
      {{{body}}}
    </div>
    <br>
    <br>
  </body>
</html>

El archivo main.hbs está en la carpeta layout. Debe contener cualquier código HTML que sea necesario en toda la aplicación. Cualquier interfaz de usuario que cambie de una vista a otra, como en signin.hbs, se coloca en el marcador de posición que se muestra como {{{body}}}.

El archivo main.hbs implementa la interfaz de usuario creada con el marco CSS Bootstrap 5. Verá los componentes de la interfaz de usuario Editar contraseña, Restablecer contraseña y Cerrar sesión (botones) cuando haya iniciado sesión. Verá Iniciar sesión cuando cierre la sesión. La variable booleana showSignInButton, enviada por el servidor de aplicaciones, controla este comportamiento.

En el archivo signin.hbs, agregue el código siguiente:

<div class="col-md-3" style="margin:auto">
  <div class="card text-center">
    <div class="card-body">
      {{#if showSignInButton}}
          <h5 class="card-title">Please sign-in to acquire an ID token</h5>
      {{else}}
           <h5 class="card-title">You have signed in</h5>
      {{/if}}
    </div>

    <div class="card-body">
      {{#if message}}
          <h5 class="card-title text-danger">{{message}}</h5>
      {{/if}}
    </div>
  </div>
</div>

Paso 4: Configuración del servidor web y el cliente MSAL

  1. En el archivo .env, agregue el código siguiente y actualícelo como se explica en Configuración de la aplicación web de ejemplo.

    #HTTP port
    SERVER_PORT=3000
    #web apps client ID
    APP_CLIENT_ID=<You app client ID here>
    #session secret
    SESSION_SECRET=sessionSecretHere
    #web app client secret
    APP_CLIENT_SECRET=<Your app client secret here>
    #B2C sign up and sign in user flow/policy authority
    SIGN_UP_SIGN_IN_POLICY_AUTHORITY=https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<sign-in-sign-up-user-flow-name>
    #B2C password reset user flow/policy authority
    RESET_PASSWORD_POLICY_AUTHORITY=https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<reset-password-user-flow-name>
    #B2C edit profile user flow/policy authority
    EDIT_PROFILE_POLICY_AUTHORITY=https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<profile-edit-user-flow-name>
    #B2C authority domain
    AUTHORITY_DOMAIN=https://<your-tenant-name>.b2clogin.com
    #client redirect url
    APP_REDIRECT_URI=http://localhost:3000/redirect
    #Logout endpoint 
    LOGOUT_ENDPOINT=https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<sign-in-sign-up-user-flow-name>/oauth2/v2.0/logout?post_logout_redirect_uri=http://localhost:3000
    
  2. En el archivo index.js, agregue el código siguiente para usar las dependencias de la aplicación:

    require('dotenv').config();
    const express = require('express');
    const session = require('express-session');
    const {engine}  = require('express-handlebars');
    const msal = require('@azure/msal-node');
    
  3. En el archivo index.js, agregue el código siguiente para configurar la biblioteca de autenticación:

    /**
     * Confidential Client Application Configuration
     */
     const confidentialClientConfig = {
        auth: {
            clientId: process.env.APP_CLIENT_ID, 
            authority: process.env.SIGN_UP_SIGN_IN_POLICY_AUTHORITY, 
            clientSecret: process.env.APP_CLIENT_SECRET,
            knownAuthorities: [process.env.AUTHORITY_DOMAIN], //This must be an array
            redirectUri: process.env.APP_REDIRECT_URI,
            validateAuthority: false
        },
        system: {
            loggerOptions: {
                loggerCallback(loglevel, message, containsPii) {
                    console.log(message);
                },
                piiLoggingEnabled: false,
                logLevel: msal.LogLevel.Verbose,
            }
        }
    };
    
    // Initialize MSAL Node
    const confidentialClientApplication = new msal.ConfidentialClientApplication(confidentialClientConfig);
    

    confidentialClientConfig es el objeto de configuración de MSAL que se usa para conectarse a los puntos de conexión de autenticación del inquilino de Azure AD B2C.

  4. Para agregar más variables globales en el archivo index.js, agregue el código siguiente:

    /**
     * The MSAL.js library allows you to pass your custom state as state parameter in the Request object
     * By default, MSAL.js passes a randomly generated unique state parameter value in the authentication requests.
     * The state parameter can also be used to encode information of the app's state before redirect. 
     * You can pass the user's state in the app, such as the page or view they were on, as input to this parameter.
     * For more information, visit: https://docs.microsoft.com/azure/active-directory/develop/msal-js-pass-custom-state-authentication-request
     * In this scenario, the states also serve to show the action that was requested of B2C since only one redirect URL is possible. 
     */
    
    const APP_STATES = {
        LOGIN: 'login',
        LOGOUT: 'logout',
        PASSWORD_RESET: 'password_reset',
        EDIT_PROFILE : 'update_profile'
    }
    
    
    /** 
     * Request Configuration
     * We manipulate these two request objects below 
     * to acquire a token with the appropriate claims.
     */
     const authCodeRequest = {
        redirectUri: confidentialClientConfig.auth.redirectUri,
    };
    
    const tokenRequest = {
        redirectUri: confidentialClientConfig.auth.redirectUri,
    };
    
    /**
     * Using express-session middleware. Be sure to familiarize yourself with available options
     * and set them as desired. Visit: https://www.npmjs.com/package/express-session
     */
     const sessionConfig = {
        secret: process.env.SESSION_SECRET,
        resave: false,
        saveUninitialized: false,
        cookie: {
            secure: false, // set this to true on production
        }
    }
    
    1. APP_STATES: se usa para diferenciar entre las respuestas recibidas de Azure AD B2C mediante el etiquetado de las solicitudes. Solo hay un identificador URI de redirección para cualquier número de solicitudes enviadas a Azure AD B2C.
    2. authCodeRequest: objeto de configuración que se usa para recuperar el código de autorización.
    3. tokenRequest: objeto de configuración que se usa para adquirir un token mediante el código de autorización.
    4. sessionConfig: objeto de configuración de la sesión de Express.
  5. Para establecer el motor de plantillas de las vistas y la configuración de la sesión de Express, agregue el código siguiente en el archivo index.js:

     
    //Create an express instance
    const app = express();
    
    //Set handlebars as your view engine
    app.engine('.hbs', engine({extname: '.hbs'}));
    app.set('view engine', '.hbs');
    app.set("views", "./views");
    
    //usse session configuration 
    app.use(session(sessionConfig));
    

Paso 5: Adición de rutas rápidas

Antes de agregar la ruta de la aplicación, agregue la lógica que recupera la dirección URL del código de autorización, que es el primer fragmento del flujo de concesión de código de autorización. En el archivo index.js, agregue el código siguiente:


/**
 * This method is used to generate an auth code request
 * @param {string} authority: the authority to request the auth code from 
 * @param {array} scopes: scopes to request the auth code for 
 * @param {string} state: state of the application
 * @param {Object} res: express middleware response object
 */
 const getAuthCode = (authority, scopes, state, res) => {

    // prepare the request
    console.log("Fetching Authorization code")
    authCodeRequest.authority = authority;
    authCodeRequest.scopes = scopes;
    authCodeRequest.state = state;

    //Each time you fetch Authorization code, update the relevant authority in the tokenRequest configuration
    tokenRequest.authority = authority;

    // request an authorization code to exchange for a token
    return confidentialClientApplication.getAuthCodeUrl(authCodeRequest)
        .then((response) => {
            console.log("\nAuthCodeURL: \n" + response);
            //redirect to the auth code URL/send code to 
            res.redirect(response);
        })
        .catch((error) => {
            res.status(500).send(error);
        });
}

El objeto authCodeRequest tiene las propiedades redirectUri, authority, scopes y state. El objeto se pasa como un parámetro al método getAuthCodeUrl.

En el archivo index.js, agregue el código siguiente:

 app.get('/', (req, res) => {
    res.render('signin', { showSignInButton: true });
});

app.get('/signin',(req, res)=>{
        //Initiate a Auth Code Flow >> for sign in
        //no scopes passed. openid, profile and offline_access will be used by default.
        getAuthCode(process.env.SIGN_UP_SIGN_IN_POLICY_AUTHORITY, [], APP_STATES.LOGIN, res);
});

/**
 * Change password end point
*/
app.get('/password',(req, res)=>{
    getAuthCode(process.env.RESET_PASSWORD_POLICY_AUTHORITY, [], APP_STATES.PASSWORD_RESET, res); 
});

/**
 * Edit profile end point
*/
app.get('/profile',(req, res)=>{
    getAuthCode(process.env.EDIT_PROFILE_POLICY_AUTHORITY, [], APP_STATES.EDIT_PROFILE, res); 
});

/**
 * Sign out end point
*/
app.get('/signout',async (req, res)=>{    
    logoutUri = process.env.LOGOUT_ENDPOINT;
    req.session.destroy(() => {
        //When session destruction succeeds, notify B2C service using the logout uri.
        res.redirect(logoutUri);
    });
});

app.get('/redirect',(req, res)=>{
    
    //determine the reason why the request was sent by checking the state
    if (req.query.state === APP_STATES.LOGIN) {
        //prepare the request for authentication        
        tokenRequest.code = req.query.code;
        confidentialClientApplication.acquireTokenByCode(tokenRequest).then((response)=>{
        
        req.session.sessionParams = {user: response.account, idToken: response.idToken};
        console.log("\nAuthToken: \n" + JSON.stringify(response));
        res.render('signin',{showSignInButton: false, givenName: response.account.idTokenClaims.given_name});
        }).catch((error)=>{
            console.log("\nErrorAtLogin: \n" + error);
        });
    }else if (req.query.state === APP_STATES.PASSWORD_RESET) {
        //If the query string has a error param
        if (req.query.error) {
            //and if the error_description contains AADB2C90091 error code
            //Means user selected the Cancel button on the password reset experience 
            if (JSON.stringify(req.query.error_description).includes('AADB2C90091')) {
                //Send the user home with some message
                //But always check if your session still exists
                res.render('signin', {showSignInButton: false, givenName: req.session.sessionParams.user.idTokenClaims.given_name, message: 'User has cancelled the operation'});
            }
        }else{
            
            res.render('signin', {showSignInButton: false, givenName: req.session.sessionParams.user.idTokenClaims.given_name});
        }        
        
    }else if (req.query.state === APP_STATES.EDIT_PROFILE){
    
        tokenRequest.scopes = [];
        tokenRequest.code = req.query.code;
        
        //Request token with claims, including the name that was updated.
        confidentialClientApplication.acquireTokenByCode(tokenRequest).then((response)=>{
            req.session.sessionParams = {user: response.account, idToken: response.idToken};
            console.log("\AuthToken: \n" + JSON.stringify(response));
            res.render('signin',{showSignInButton: false, givenName: response.account.idTokenClaims.given_name});
        }).catch((error)=>{
            //Handle error
        });
    }else{
        res.status(500).send('We do not recognize this response!');
    }

});

Las rutas rápidas son:

  • /:
    • Se usa para entrar a la aplicación web.
    • Representa la página signin.
  • /signin:
    • Se usa cuando inicia sesión.
    • Llama al método getAuthCode() y pasa el elemento authority para el flujo de usuario y la directiva de inicio de sesión y registro, APP_STATES.LOGIN, y una matriz scopes vacía.
    • Si es necesario, se produce un desafío para que escriba sus credenciales. Si no tiene una cuenta, se le pedirá que se registre.
    • La respuesta final que resulta de esta ruta incluye un código de autorización de Azure AD B2C que se devuelve a la ruta /redirect.
  • /password:
    • Se usa al restablecer una contraseña.
    • Llama al método getAuthCode() y pasa el elemento authority para el flujo de usuario y la directiva de restablecimiento de contraseña, APP_STATES.PASSWORD_RESET, y una matriz scopes vacía.
    • Le permite cambiar la contraseña mediante la experiencia de restablecimiento de contraseña, o bien pueden cancelar la operación.
    • La respuesta final que resulta de esta ruta incluye un código de autorización de Azure AD B2C que se devuelve a la ruta /redirect. Si cancela la operación, se devuelve un error.
  • /profile:
    • Se usa al actualizar el perfil.
    • Llama al método getAuthCode() y pasa el elemento authority para el flujo de usuario y la directiva de edición de perfil, APP_STATES.EDIT_PROFILE, y una matriz scopes vacía.
    • Le permite actualizar el perfil y usar la experiencia de edición de perfiles.
    • La respuesta final que resulta de esta ruta incluye un código de autorización de Azure AD B2C que se devuelve a la ruta /redirect.
  • /signout:
    • Se usa al cerrar sesión.
    • La aplicación web borra la sesión y realiza una llamada HTTP al punto de conexión de cierre de sesión de Azure AD B2C.
  • /redirect:
    • Se trata de la ruta establecida como URI de redireccionamiento para la aplicación web en Azure Portal.
    • Usa el parámetro de consulta state de la solicitud de Azure AD B2C a fin de diferenciar entre las solicitudes que se realizan desde la aplicación web. Controla todas las redirecciones desde Azure AD B2C, excepto para el cierre de sesión.
    • Si el estado de la aplicación es APP_STATES.LOGIN, el código de autorización adquirido se usa para recuperar un token mediante el método acquireTokenByCode(). Este token incluye los elementos idToken y idTokenClaims, que se usan para la identificación del usuario.
    • Si el estado de la aplicación es APP_STATES.PASSWORD_RESET, controla cualquier error, como user cancelled the operation. El código de error AADB2C90091 identifica este error. De lo contrario, decide la siguiente experiencia del usuario.
    • Si el estado de la aplicación es APP_STATES.EDIT_PROFILE, usa el código de autorización para adquirir un token. El token contiene el elemento idTokenClaims, que incluye los nuevos cambios.

Paso 6: Inicio del servidor de Node

Para iniciar el servidor de Node, agregue el código siguiente en el archivo index.js:

app.listen(process.env.SERVER_PORT, () => {
    console.log(`Msal Node Auth Code Sample app listening on port !` + process.env.SERVER_PORT);
});

Después de realizar todos los cambios necesarios en el archivo index.js, debería ser similar al siguiente archivo:

/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */
 //<ms_docref_use_app_dependencies>
require('dotenv').config();
const express = require('express');
const session = require('express-session');
const {engine}  = require('express-handlebars');
const msal = require('@azure/msal-node');
//</ms_docref_use_app_dependencies>

//<ms_docref_configure_msal>
/**
 * Confidential Client Application Configuration
 */
 const confidentialClientConfig = {
    auth: {
        clientId: process.env.APP_CLIENT_ID, 
        authority: process.env.SIGN_UP_SIGN_IN_POLICY_AUTHORITY, 
        clientSecret: process.env.APP_CLIENT_SECRET,
        knownAuthorities: [process.env.AUTHORITY_DOMAIN], //This must be an array
        redirectUri: process.env.APP_REDIRECT_URI,
        validateAuthority: false
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
};

// Initialize MSAL Node
const confidentialClientApplication = new msal.ConfidentialClientApplication(confidentialClientConfig);
//</ms_docref_configure_msal>

//<ms_docref_global_variable>
/**
 * The MSAL.js library allows you to pass your custom state as state parameter in the Request object
 * By default, MSAL.js passes a randomly generated unique state parameter value in the authentication requests.
 * The state parameter can also be used to encode information of the app's state before redirect. 
 * You can pass the user's state in the app, such as the page or view they were on, as input to this parameter.
 * For more information, visit: https://docs.microsoft.com/azure/active-directory/develop/msal-js-pass-custom-state-authentication-request
 * In this scenario, the states also serve to show the action that was requested of B2C since only one redirect URL is possible. 
 */

const APP_STATES = {
    LOGIN: 'login',
    LOGOUT: 'logout',
    PASSWORD_RESET: 'password_reset',
    EDIT_PROFILE : 'update_profile'
}


/** 
 * Request Configuration
 * We manipulate these two request objects below 
 * to acquire a token with the appropriate claims.
 */
 const authCodeRequest = {
    redirectUri: confidentialClientConfig.auth.redirectUri,
};

const tokenRequest = {
    redirectUri: confidentialClientConfig.auth.redirectUri,
};

/**
 * Using express-session middleware. Be sure to familiarize yourself with available options
 * and set them as desired. Visit: https://www.npmjs.com/package/express-session
 */
 const sessionConfig = {
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false, // set this to true on production
    }
}

//</ms_docref_global_variable>

//<ms_docref_view_tepmplate_engine>
 
//Create an express instance
const app = express();

//Set handlebars as your view engine
app.engine('.hbs', engine({extname: '.hbs'}));
app.set('view engine', '.hbs');
app.set("views", "./views");

//usse session configuration 
app.use(session(sessionConfig));

//</ms_docref_view_tepmplate_engine>

//<ms_docref_authorization_code_url>

/**
 * This method is used to generate an auth code request
 * @param {string} authority: the authority to request the auth code from 
 * @param {array} scopes: scopes to request the auth code for 
 * @param {string} state: state of the application
 * @param {Object} res: express middleware response object
 */
 const getAuthCode = (authority, scopes, state, res) => {

    // prepare the request
    console.log("Fetching Authorization code")
    authCodeRequest.authority = authority;
    authCodeRequest.scopes = scopes;
    authCodeRequest.state = state;

    //Each time you fetch Authorization code, update the relevant authority in the tokenRequest configuration
    tokenRequest.authority = authority;

    // request an authorization code to exchange for a token
    return confidentialClientApplication.getAuthCodeUrl(authCodeRequest)
        .then((response) => {
            console.log("\nAuthCodeURL: \n" + response);
            //redirect to the auth code URL/send code to 
            res.redirect(response);
        })
        .catch((error) => {
            res.status(500).send(error);
        });
}
 
 //</ms_docref_authorization_code_url>

 
 //<ms_docref_app_endpoints>
 app.get('/', (req, res) => {
    res.render('signin', { showSignInButton: true });
});

app.get('/signin',(req, res)=>{
        //Initiate a Auth Code Flow >> for sign in
        //no scopes passed. openid, profile and offline_access will be used by default.
        getAuthCode(process.env.SIGN_UP_SIGN_IN_POLICY_AUTHORITY, [], APP_STATES.LOGIN, res);
});

/**
 * Change password end point
*/
app.get('/password',(req, res)=>{
    getAuthCode(process.env.RESET_PASSWORD_POLICY_AUTHORITY, [], APP_STATES.PASSWORD_RESET, res); 
});

/**
 * Edit profile end point
*/
app.get('/profile',(req, res)=>{
    getAuthCode(process.env.EDIT_PROFILE_POLICY_AUTHORITY, [], APP_STATES.EDIT_PROFILE, res); 
});

/**
 * Sign out end point
*/
app.get('/signout',async (req, res)=>{    
    logoutUri = process.env.LOGOUT_ENDPOINT;
    req.session.destroy(() => {
        //When session destruction succeeds, notify B2C service using the logout uri.
        res.redirect(logoutUri);
    });
});

app.get('/redirect',(req, res)=>{
    
    //determine the reason why the request was sent by checking the state
    if (req.query.state === APP_STATES.LOGIN) {
        //prepare the request for authentication        
        tokenRequest.code = req.query.code;
        confidentialClientApplication.acquireTokenByCode(tokenRequest).then((response)=>{
        
        req.session.sessionParams = {user: response.account, idToken: response.idToken};
        console.log("\nAuthToken: \n" + JSON.stringify(response));
        res.render('signin',{showSignInButton: false, givenName: response.account.idTokenClaims.given_name});
        }).catch((error)=>{
            console.log("\nErrorAtLogin: \n" + error);
        });
    }else if (req.query.state === APP_STATES.PASSWORD_RESET) {
        //If the query string has a error param
        if (req.query.error) {
            //and if the error_description contains AADB2C90091 error code
            //Means user selected the Cancel button on the password reset experience 
            if (JSON.stringify(req.query.error_description).includes('AADB2C90091')) {
                //Send the user home with some message
                //But always check if your session still exists
                res.render('signin', {showSignInButton: false, givenName: req.session.sessionParams.user.idTokenClaims.given_name, message: 'User has cancelled the operation'});
            }
        }else{
            
            res.render('signin', {showSignInButton: false, givenName: req.session.sessionParams.user.idTokenClaims.given_name});
        }        
        
    }else if (req.query.state === APP_STATES.EDIT_PROFILE){
    
        tokenRequest.scopes = [];
        tokenRequest.code = req.query.code;
        
        //Request token with claims, including the name that was updated.
        confidentialClientApplication.acquireTokenByCode(tokenRequest).then((response)=>{
            req.session.sessionParams = {user: response.account, idToken: response.idToken};
            console.log("\AuthToken: \n" + JSON.stringify(response));
            res.render('signin',{showSignInButton: false, givenName: response.account.idTokenClaims.given_name});
        }).catch((error)=>{
            //Handle error
        });
    }else{
        res.status(500).send('We do not recognize this response!');
    }

});

 //</ms_docref_app_endpoints>
//start app server to listen on set port
 //<ms_docref_start_node_server>
app.listen(process.env.SERVER_PORT, () => {
    console.log(`Msal Node Auth Code Sample app listening on port !` + process.env.SERVER_PORT);
});
//</ms_docref_start_node_server>

Paso 7: Ejecución de la aplicación web

Siga los pasos descritos en Ejecución de la aplicación web para probar la aplicación web Node.js.

Pasos siguientes