تمكين المصادقة في تطبيق الويب Node الخاص بك باستخدام Microsoft Azure Active Directory B2C

في هذه المقالة، ستتعرف على كيفية إضافة مصادقة Microsoft Azure Active Directory B2C (Microsoft Azure AD B2C) في تطبيق الويب Node.js الخاص بك. ستقوم بتمكين المستخدمين من تسجيل الدخول وتسجيل الخروج وتحديث ملفات التعريف وإعادة تعيين كلمات المرور باستخدام تدفقات مستخدم Microsoft Azure AD B2C. تستخدم هذه المقالة Microsoft Authentication Library (MSAL) لـ Node لتبسيط إضافة مصادقة إلى تطبيق الويب Node.

الهدف من هذه المقالة هو استبدال نموذج التطبيق الذي استخدمته في تكوين المصادقة في نموذج تطبيق ويب Node.js باستخدام Microsoft Azure AD B2C في تطبيق الويب Node.js الخاص بك.

تستخدم هذه المقالة Node.js وExpress لإنشاء تطبيق ويب Node.js أساسي. تستخدم طرق عرض التطبيق Handlebars.

المتطلبات الأساسية

الخطوة 1: إنشاء مشروع node

قم بإنشاء مجلد لاستضافة تطبيق العقدة الخاص بك، مثل active-directory-b2c-msal-node-sign-in-sign-out-webapp.

  1. في المحطة الطرفية، قم بتغيير الدليل إلى مجلد تطبيق العقدة، مثل cd active-directory-b2c-msal-node-sign-in-sign-out-webapp، ثم قم بالتشغيل npm init -y. ينشئ هذا الأمر ملف افتراضي package.json للمشروع Node.js.

  2. في محطتك الطرفية، قم بـ «تشغيل npm install express». يثبّت هذا الأمر إطار عمل Express.

  3. إنشاء المزيد من المجلدات والملفات لتحقيق بنية المشروع التالية:

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

يحتوي views المجلد على ملفات المقود لواجهة مستخدم تطبيق الويب.

الخطوة 2: تثبيت تبعيات التطبيق

في المحطة الطرفية، قم بتثبيت الحزم dotenv وexpress-handlebars وexpress-session و@azure/msal-node عن طريق تشغيل الأوامر التالية:

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

الخطوة 3: إنشاء مكونات واجهة المستخدم للتطبيق

أضف التعليمة البرمجية التالية في ملف ⁧main.hbs⁩:

<!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>

main.hbsالملف في layout المجلد. يجب أن يحتوي على أي رمز HTML مطلوب في جميع أنحاء التطبيق الخاص بك. يتم وضع أي واجهة مستخدم تتغير من طريقة عرض إلى أخرى، كما هو الحال في signin.hbs، في العنصر النائب الذي يظهر كـ {{{body}}}.

main.hbsالملف بتنفيذ واجهة المستخدم بنيت مع إطار Bootstrap 5 CSS. سترى مكونات (أزرار) واجهة المستخدم Edit password، وReset password، وSign out عند تسجيل الدخول. سترى Sign in عند تسجيل الخروج. ويتم التحكم في هذا السلوك بواسطة المتغير المنطقي showSignInButton، الذي يرسله خادم التطبيق.

أضف التعليمة البرمجية التالية في ملف ⁧signin.hbs⁩:

<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>

الخطوة 4: تكوين خادم ويب وعميل MSAL

  1. في الملف .env، أضِف التعليمة البرمجية التالية وحدثها كما هو موضح في تكوين نموذج تطبيق الويب.

    #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. في index.js الملف، أضف التعليمات البرمجية التالية لاستخدام تبعيات التطبيق:

    require('dotenv').config();
    const express = require('express');
    const session = require('express-session');
    const {engine}  = require('express-handlebars');
    const msal = require('@azure/msal-node');
    
  3. في ملف index.js الخاص بك، أضِف التعليمة البرمجية التالية لتكوين مكتبة المصادقة:

    /**
     * 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 هو عنصر تكوين MSAL المستخدم للاتصال بنقاط نهاية مصادقة مستأجر Microsoft Azure AD B2C.

  4. لإضافة المزيد من المتغيرات العمومية في ملف index.js، أضِف التعليمات البرمجية التالية:

    /**
     * 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: تستخدم للتمييز بين الردود الواردة من Azure AD B2C عن طريق وضع علامات على الطلبات. هناك URI واحد فقط لإعادة التوجيه لأي عدد من الطلبات المرسلة إلى Microsoft Azure AD B2C.
    2. authCodeRequest: كائن التكوين المستخدم لاسترداد رمز التخويل.
    3. tokenRequest: كائن التكوين المستخدم للحصول على رمز مميز بواسطة رمز التخويل.
    4. sessionConfig: كائن التكوين لجلسة عمل Express.
  5. لتعيين مشغل قالب العرض وتكوين جلسة عمل Express، أضف التعليمات البرمجية التالية في 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));
    

الخطوة 5: إضافة مسارات Express

قبل إضافة مسار التطبيق، أضِف المنطق الذي يسترد عنوان URL لرمز التخويل، وهو المرحلة الأولى من تدفق منح رمز التخويل. أضف التعليمة البرمجية التالية في ملف ⁧index.js⁩:


/**
 * 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);
        });
}

authCodeRequestالكائن يحتوي على خصائص redirectUriو authority و scopesو state. يتم تمرير الكائن إلى getAuthCodeUrl الأسلوب كمعلمة.

أضف التعليمة البرمجية التالية في ملف ⁧index.js⁩:

 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!');
    }

});

مسارات Express هي:

  • /:
    • يستخدم لإدخال تطبيق الويب.
    • فإنه يجعل signin الصفحة.
  • /signin:
    • يُستخدم عند تسجيل الدخول.
    • يستدعي getAuthCode() الأسلوب ويمر authority لـتسجيل الدخول والاشتراك تدفق المستخدم/النهج، APP_STATES.LOGINو صفيف فارغ scopes إليه.
    • إذا لزم الأمر، فإنه يسبب تحدياً لإدخال بيانات الاعتماد الخاصة بك. إذا لم يكن لديك حساب، فسيطالبك بالتسجيل.
    • تتضمن الاستجابة النهائية التي تنتج عن هذا المسار رمز تخويل من Microsoft Azure AD B2C تم نشره مرة أخرى إلى المسار /redirect.
  • /password:
    • يُستخدم عند إعادة تعيين كلمة مرور.
    • فإنه يستدعي getAuthCode()الطريقة ويمررauthority لسياسة أو تدفق المستخدم لإعادة تعيين كلمة المرور، APP_STATES.PASSWORD_RESETومجموعة فارغةscopes لها.
    • يمكّنك من تغيير كلمة المرور الخاصة بك باستخدام تجربة إعادة تعيين كلمة المرور، أو يمكنهم إلغاء العملية.
    • تتضمن الاستجابة النهائية التي تنتج عن هذا المسار رمز تخويل من Microsoft Azure AD B2C تم نشره مرة أخرى إلى المسار /redirect. إذا قمت بإلغاء العملية، يُعاد نشر الخطأ.
  • /profile:
    • يُستخدم عند تحديث ملف التعريف الخاص بك.
    • فإنه يستدعي getAuthCode()الطريقة ويمررauthority لسياسة أو تدفق المستخدم لتحرير الملف الشخصي، APP_STATES.EDIT_PROFILEومجموعة فارغةscopes لها.
    • يمكّنك من تحديث ملف التعريف الخاص بك، واستخدام تجربة تحرير ملف التعريف.
    • تتضمن الاستجابة النهائية التي تنتج عن هذا المسار رمز تخويل من Microsoft Azure AD B2C تم نشره مرة أخرى إلى المسار /redirect.
  • /signout:
    • يُستخدم عند تسجيل الخروج.
    • يمسح تطبيق الويب الجلسة، ويستدعي HTTP إلى نقطة نهاية تسجيل الخروج Microsoft Azure AD B2C.
  • /redirect:
    • يمثل المسار الذي تم تعيينه كـ Redirect URI لتطبيق الويب في مدخل Microsoft Azure.
    • يستخدم معلمة الاستعلام state في الطلب من Microsoft Azure AD B2C للتمييز بين الطلبات التي يتم إجراؤها من تطبيق الويب. يعالج جميع عمليات إعادة التوجيه من Microsoft Azure AD B2C، باستثناء تسجيل الخروج.
    • إذا كانت حالة التطبيق APP_STATES.LOGIN، يتم استخدام رمز التخويل المكتسب لاسترداد رمز مميز من خلال acquireTokenByCode() الأسلوب. يتضمن هذا الرمز المميز idToken و، والذي يتم idTokenClaims استخدامه لتعريف المستخدم.
    • إذا كانت حالة التطبيق APP_STATES.PASSWORD_RESET،فإنه يعالج أي خطأ، مثل user cancelled the operation. يحدد رمز الخطأ AADB2C90091 هذا الخطأ. وإلا، فإنه يقرر تجربة المستخدم التالي.
    • إذا كانت حالة التطبيق APP_STATES.EDIT_PROFILE، فإنه يستخدم رمز التخويل للحصول على رمز مميز. يحتوي الرمز المميز علىidTokenClaims، والذي يتضمن التغييرات الجديدة.

الخطوة 6: بدء تشغيل خادم Node

لبدء تشغيل خادم العقدة، أضف التعليمات البرمجية التالية في index.js الملف:

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

بعد إجراء جميع التغييرات المطلوبة في الملف index.js، يجب أن يبدو مشابهاً للملف التالي:

/*
 * 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>

الخطوة 7: تشغيل تطبيق الويب

اتبع الخطوات الواردة في تشغيل تطبيق الويب لاختبار تطبيق الويب Node.js.

الخطوات التالية