Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
применяется к: арендаторы рабочей силы
внешние клиенты (подробнее)
В этом руководстве описано, как добавить логику входа и выхода в веб-приложение Node/Express. Этот код позволяет пользователям входить в ваше клиентское приложение через внешний арендатор или сотрудникам в арендаторе рабочей силы.
Это часть 2 из серии учебников, состоящей из 3 частей.
Изучив это руководство, вы:
- Добавление логики входа и выхода
- Просмотр утверждений идентификационного токена
- Запустите приложение и проверьте возможности входа и выхода.
Необходимые условия
- Выполните шаги, указанные в руководстве : настройка веб-приложения Node.js для входа пользователей с использованием платформы удостоверений Microsoft.
Создание объекта конфигурации MSAL
В редакторе кода откройте файл authConfig.js, а затем добавьте следующий код:
require('dotenv').config();
const TENANT_SUBDOMAIN = process.env.TENANT_SUBDOMAIN || 'Enter_the_Tenant_Subdomain_Here';
const REDIRECT_URI = process.env.REDIRECT_URI || 'http://localhost:3000/auth/redirect';
const POST_LOGOUT_REDIRECT_URI = process.env.POST_LOGOUT_REDIRECT_URI || 'http://localhost:3000';
const GRAPH_ME_ENDPOINT = process.env.GRAPH_API_ENDPOINT + "v1.0/me" || 'Enter_the_Graph_Endpoint_Here';
/**
* Configuration object to be passed to MSAL instance on creation.
* For a full list of MSAL Node configuration parameters, visit:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/configuration.md
*/
const msalConfig = {
auth: {
clientId: process.env.CLIENT_ID || 'Enter_the_Application_Id_Here', // 'Application (client) ID' of app registration in Azure portal - this value is a GUID
//For external tenant
authority: process.env.AUTHORITY || `https://${TENANT_SUBDOMAIN}.ciamlogin.com/`, // replace "Enter_the_Tenant_Subdomain_Here" with your tenant name
//For workforce tenant
//authority: process.env.CLOUD_INSTANCE + process.env.TENANT_ID
clientSecret: process.env.CLIENT_SECRET || 'Enter_the_Client_Secret_Here', // Client secret generated from the app registration in Azure portal
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: 'Info',
},
},
};
module.exports = {
msalConfig,
REDIRECT_URI,
POST_LOGOUT_REDIRECT_URI,
TENANT_SUBDOMAIN,
GRAPH_ME_ENDPOINT
};
Объект msalConfig
содержит набор параметров конфигурации, используемых для настройки поведения потоков проверки подлинности.
В файле authConfig.js замените:
Enter_the_Application_Id_Here
с идентификатором приложения (клиента), зарегистрированного ранее.Enter_the_Tenant_Subdomain_Here
а затем замените его поддоменом внешней директории (арендатора). Например, если основной домен клиентаcontoso.onmicrosoft.com
, используйтеcontoso
. Если у вас нет имени арендатора, узнайте, как просматривать сведения о арендаторе. Это значение требуется только для внешнего арендатора.Используйте значение секрета приложения
Enter_the_Client_Secret_Here
, скопированное ранее.Enter_the_Graph_Endpoint_Here
с облачным экземпляром Microsoft Graph API, который ваше приложение будет вызывать. Используйте значениеhttps://graph.microsoft.com/
(не забудьте включить конечную косую черту)
Если вы используете файл .env для хранения сведений о конфигурации:
В редакторе кода откройте файл .env, а затем добавьте следующий код.
CLIENT_ID=Enter_the_Application_Id_Here TENANT_SUBDOMAIN=Enter_the_Tenant_Subdomain_Here CLOUD_INSTANCE="Enter_the_Cloud_Instance_Id_Here" # cloud instance string should end with a trailing slash TENANT_ID=Enter_the_Tenant_ID_here CLIENT_SECRET=Enter_the_Client_Secret_Here REDIRECT_URI=http://localhost:3000/auth/redirect POST_LOGOUT_REDIRECT_URI=http://localhost:3000 GRAPH_API_ENDPOINT=Enter_the_Graph_Endpoint_Here # graph api endpoint string should end with a trailing slash EXPRESS_SESSION_SECRET=Enter_the_Express_Session_Secret_Here # express session secret, just any random text
Замените заполнитель:
-
Enter_the_Application_Id_Here
,Enter_the_Tenant_Subdomain_Here
иEnter_the_Client_Secret_Here
, как описано ранее. -
Enter_the_Cloud_Instance_Id_Here
в облачном экземпляре Azure, где зарегистрировано ваше приложение. Используйтеhttps://login.microsoftonline.com/
в качестве значения (включите заключительную косую черту). Это значение необходимо только для арендатора рабочей силы. -
Enter_the_Tenant_ID_here
с идентификатором арендатора рабочей силы или основным доменом, например, aaaabbbb-0000-cccc-1111-dddd2222eeee, или contoso.microsoft.com. Это значение необходимо только для арендатора рабочей силы.
-
Вы экспортируете msalConfig
, REDIRECT_URI
, TENANT_SUBDOMAIN
, GRAPH_ME_ENDPOINT
и POST_LOGOUT_REDIRECT_URI
переменные в файле authConfig.js, чтобы сделать их доступными в других файлах.
URL-адрес авторизации для вашего приложения
Полномочия приложений для внешних и штатных пользователей различаются. Создайте их, как показано ниже:
//Authority for workforce tenant
authority: process.env.CLOUD_INSTANCE + process.env.TENANT_ID
Использование личного домена URL-адреса (необязательно)
В арендаторах рабочей среды не поддерживаются настраиваемые домены URL.
Добавление экспресс-маршрутов
Маршруты Express предоставляют конечные точки, позволяющие выполнять такие операции, как вход, выход и просмотр утверждений токена идентификатора.
Точка входа приложения
В редакторе кода откройте файл маршрутов /index.js, а затем добавьте следующий код:
const express = require('express');
const router = express.Router();
router.get('/', function (req, res, next) {
res.render('index', {
title: 'MSAL Node & Express Web App',
isAuthenticated: req.session.isAuthenticated,
username: req.session.account?.username !== '' ? req.session.account?.username : req.session.account?.name,
});
});
module.exports = router;
Маршрут /
— это точка входа в приложение. Он отображает вид views/index.hbs, который вы создали ранее в разделе Создание компонентов пользовательского интерфейса приложения.
isAuthenticated
— это логическая переменная, которая определяет, что вы видите в интерфейсе.
Вход и выход
В редакторе кода откройте файл маршрутов /auth.js, а затем добавьте следующий код:
const express = require('express'); const authController = require('../controller/authController'); const router = express.Router(); router.get('/signin', authController.signIn); router.get('/signout', authController.signOut); router.post('/redirect', authController.handleRedirect); module.exports = router;
В редакторе кода откройте файл контроллера илиauthController.js, а затем добавьте следующий код:
const authProvider = require('../auth/AuthProvider'); exports.signIn = async (req, res, next) => { return authProvider.login(req, res, next); }; exports.handleRedirect = async (req, res, next) => { return authProvider.handleRedirect(req, res, next); } exports.signOut = async (req, res, next) => { return authProvider.logout(req, res, next); };
В редакторе кода откройте файл проверки подлинности илиAuthProvider.js, а затем добавьте следующий код:
const msal = require('@azure/msal-node'); const axios = require('axios'); const { msalConfig, TENANT_SUBDOMAIN, REDIRECT_URI, POST_LOGOUT_REDIRECT_URI, GRAPH_ME_ENDPOINT} = require('../authConfig'); class AuthProvider { config; cryptoProvider; constructor(config) { this.config = config; this.cryptoProvider = new msal.CryptoProvider(); } getMsalInstance(msalConfig) { return new msal.ConfidentialClientApplication(msalConfig); } async login(req, res, next, options = {}) { // create a GUID for crsf req.session.csrfToken = this.cryptoProvider.createNewGuid(); /** * The MSAL Node library allows you to pass your custom state as state parameter in the Request object. * 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. */ const state = this.cryptoProvider.base64Encode( JSON.stringify({ csrfToken: req.session.csrfToken, redirectTo: '/', }) ); const authCodeUrlRequestParams = { state: state, /** * By default, MSAL Node will add OIDC scopes to the auth code url request. For more information, visit: * https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes */ scopes: [], }; const authCodeRequestParams = { state: state, /** * By default, MSAL Node will add OIDC scopes to the auth code request. For more information, visit: * https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes */ scopes: [], }; /** * If the current msal configuration does not have cloudDiscoveryMetadata or authorityMetadata, we will * make a request to the relevant endpoints to retrieve the metadata. This allows MSAL to avoid making * metadata discovery calls, thereby improving performance of token acquisition process. */ if (!this.config.msalConfig.auth.authorityMetadata) { const authorityMetadata = await this.getAuthorityMetadata(); this.config.msalConfig.auth.authorityMetadata = JSON.stringify(authorityMetadata); } const msalInstance = this.getMsalInstance(this.config.msalConfig); // trigger the first leg of auth code flow return this.redirectToAuthCodeUrl( req, res, next, authCodeUrlRequestParams, authCodeRequestParams, msalInstance ); } async handleRedirect(req, res, next) { const authCodeRequest = { ...req.session.authCodeRequest, code: req.body.code, // authZ code codeVerifier: req.session.pkceCodes.verifier, // PKCE Code Verifier }; try { const msalInstance = this.getMsalInstance(this.config.msalConfig); msalInstance.getTokenCache().deserialize(req.session.tokenCache); const tokenResponse = await msalInstance.acquireTokenByCode(authCodeRequest, req.body); req.session.tokenCache = msalInstance.getTokenCache().serialize(); req.session.idToken = tokenResponse.idToken; req.session.account = tokenResponse.account; req.session.isAuthenticated = true; const state = JSON.parse(this.cryptoProvider.base64Decode(req.body.state)); res.redirect(state.redirectTo); } catch (error) { next(error); } } async logout(req, res, next) { /** * Construct a logout URI and redirect the user to end the * session with Microsoft Entra ID. For more information, visit: * https://docs.microsoft.com/azure/active-directory/develop/v2-protocols-oidc#send-a-sign-out-request */ //For external tenant //const logoutUri = `${this.config.msalConfig.auth.authority}${TENANT_SUBDOMAIN}.onmicrosoft.com/oauth2/v2.0/logout?post_logout_redirect_uri=${this.config.postLogoutRedirectUri}`; //For workforce tenant let logoutUri = `${this.config.msalConfig.auth.authority}/oauth2/v2.0/logout?post_logout_redirect_uri=${this.config.postLogoutRedirectUri}`; req.session.destroy(() => { res.redirect(logoutUri); }); } /** * Prepares the auth code request parameters and initiates the first leg of auth code flow * @param req: Express request object * @param res: Express response object * @param next: Express next function * @param authCodeUrlRequestParams: parameters for requesting an auth code url * @param authCodeRequestParams: parameters for requesting tokens using auth code */ async redirectToAuthCodeUrl(req, res, next, authCodeUrlRequestParams, authCodeRequestParams, msalInstance) { // Generate PKCE Codes before starting the authorization flow const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes(); // Set generated PKCE codes and method as session vars req.session.pkceCodes = { challengeMethod: 'S256', verifier: verifier, challenge: challenge, }; /** * By manipulating the request objects below before each request, we can obtain * auth artifacts with desired claims. For more information, visit: * https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationurlrequest * https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationcoderequest **/ req.session.authCodeUrlRequest = { ...authCodeUrlRequestParams, redirectUri: this.config.redirectUri, responseMode: 'form_post', // recommended for confidential clients codeChallenge: req.session.pkceCodes.challenge, codeChallengeMethod: req.session.pkceCodes.challengeMethod, }; req.session.authCodeRequest = { ...authCodeRequestParams, redirectUri: this.config.redirectUri, code: '', }; try { const authCodeUrlResponse = await msalInstance.getAuthCodeUrl(req.session.authCodeUrlRequest); res.redirect(authCodeUrlResponse); } catch (error) { next(error); } } /** * Retrieves oidc metadata from the openid endpoint * @returns */ async getAuthorityMetadata() { // For external tenant //const endpoint = `${this.config.msalConfig.auth.authority}${TENANT_SUBDOMAIN}.onmicrosoft.com/v2.0/.well-known/openid-configuration`; // For workforce tenant const endpoint = `${this.config.msalConfig.auth.authority}/v2.0/.well-known/openid-configuration`; try { const response = await axios.get(endpoint); return await response.data; } catch (error) { console.log(error); } } } const authProvider = new AuthProvider({ msalConfig: msalConfig, redirectUri: REDIRECT_URI, postLogoutRedirectUri: POST_LOGOUT_REDIRECT_URI, }); module.exports = authProvider;
Маршруты
/signin
,/signout
и/redirect
определены в файле маршрутов/auth.js, а их логика реализуется в классе auth/AuthProvider.js.
Метод
login
обрабатывает маршрут/signin
:Он инициирует поток входа, активируя первый этап потока кода проверки подлинности.
Он инициализирует экземпляр конфиденциального клиентского приложения с помощью объекта конфигурации MSAL,
msalConfig
, созданного ранее.const msalInstance = this.getMsalInstance(this.config.msalConfig);
Метод
getMsalInstance
определяется следующим образом:getMsalInstance(msalConfig) { return new msal.ConfidentialClientApplication(msalConfig); }
Первый этап потока кода проверки подлинности создает URL-адрес запроса кода авторизации, а затем перенаправляется на этот URL-адрес, чтобы получить код авторизации. Этот первый этап реализуется в методе
redirectToAuthCodeUrl
. Обратите внимание, что мы используем MSALs метод getAuthCodeUrl для создания URL-адреса кода авторизации://... const authCodeUrlResponse = await msalInstance.getAuthCodeUrl(req.session.authCodeUrlRequest); //...
Затем мы перенаправляем на URL-адрес кода авторизации.
//... res.redirect(authCodeUrlResponse); //...
Метод
handleRedirect
обрабатывает маршрут/redirect
:Этот URL-адрес был установлен как URI перенаправления для веб-приложения в Центре администрирования Microsoft Entra ранее в разделе "Краткое руководство: Вход пользователей в пример веб-приложение".
Эта конечная точка реализует второй этап потока кода проверки подлинности. Он использует код авторизации для запроса ID-токена с помощью метода MSAL acquireTokenByCode.
//... const tokenResponse = await msalInstance.acquireTokenByCode(authCodeRequest, req.body); //...
Получив ответ, вы можете создать сеанс Express и сохранить в нем все необходимые сведения. Необходимо включить
isAuthenticated
и установить для него значениеtrue
://... req.session.idToken = tokenResponse.idToken; req.session.account = tokenResponse.account; req.session.isAuthenticated = true; //...
Метод
logout
обрабатывает маршрут/signout
:async logout(req, res, next) { /** * Construct a logout URI and redirect the user to end the * session with Azure AD. For more information, visit: * https://docs.microsoft.com/azure/active-directory/develop/v2-protocols-oidc#send-a-sign-out-request */ const logoutUri = `${this.config.msalConfig.auth.authority}${TENANT_SUBDOMAIN}.onmicrosoft.com/oauth2/v2.0/logout?post_logout_redirect_uri=${this.config.postLogoutRedirectUri}`; req.session.destroy(() => { res.redirect(logoutUri); }); }
Он инициирует запрос на выход.
Если вы хотите выйти пользователя из приложения, недостаточно просто завершить его сеанс. Необходимо перенаправить пользователя на logoutUri. В противном случае пользователь может повторно выполнить проверку подлинности в приложениях без повторного ввода учетных данных. Если имя вашего арендатора contoso, то logoutUri выглядит примерно так же, как
https://contoso.ciamlogin.com/contoso.onmicrosoft.com/oauth2/v2.0/logout?post_logout_redirect_uri=http://localhost:3000
.
URI разлогина и конечная точка метаданных авторизации для вашего приложения
URI выхода приложения, logoutUri
и конечная точка метаданных полномочий, endpoint
для внешних и рабочих арендаторов выглядят иначе. Создайте их, как показано ниже:
//Logout URI for workforce tenant
const logoutUri = `${this.config.msalConfig.auth.authority}/oauth2/v2.0/logout?post_logout_redirect_uri=${this.config.postLogoutRedirectUri}`;
//authority metadata endpoint for workforce tenant
const endpoint = `${this.config.msalConfig.auth.authority}/v2.0/.well-known/openid-configuration`;
Просмотр утверждений идентификационного токена
В редакторе кода откройте файл маршрутов /users.js, а затем добавьте следующий код:
const express = require('express');
const router = express.Router();
// custom middleware to check auth state
function isAuthenticated(req, res, next) {
if (!req.session.isAuthenticated) {
return res.redirect('/auth/signin'); // redirect to sign-in route
}
next();
};
router.get('/id',
isAuthenticated, // check if user is authenticated
async function (req, res, next) {
res.render('id', { idTokenClaims: req.session.account.idTokenClaims });
}
);
module.exports = router;
Если пользователь прошел проверку подлинности, маршрут /id
отображает утверждения маркера идентификатора с помощью представления /views/id.hbs. Вы добавили это представление ранее в Сборка компонентов пользовательского интерфейса приложения.
Чтобы извлечь определенное утверждение маркера идентификатора, например заданное имя:
const givenName = req.session.account.idTokenClaims.given_name
Завершение работы веб-приложения
В редакторе кода откройте файл app.js, а затем добавьте в него код из app.js.
В редакторе кода откройте файл server.js, а затем добавьте в него код из server.js.
В редакторе кода откройте файл package.json, а затем обновите свойство
scripts
следующим образом:"scripts": { "start": "node server.js" }
Запуск и проверка веб-приложения Node/Express.js
На этом этапе вы можете протестировать веб-приложение узла.
Выполните действия, описанные в Создание нового пользователя для создания тестового пользователя в клиенте рабочей силы. Если у вас нет доступа к клиенту, попросите администратора клиента создать пользователя.
Чтобы запустить сервер, выполните следующие команды из каталога проекта:
npm start
Откройте браузер, а затем перейдите к
http://localhost:3000
. Вы увидите страницу, аналогичную следующему снимку экрана:Выберите Войдите в систему, чтобы запустить процесс входа. При первом входе вам будет предложено предоставить согласие на вход и доступ к вашему профилю, как показано на следующем снимке экрана:
После успешного входа вы будете перенаправлены обратно на домашнюю страницу приложения.
Следующий шаг
Руководство: вызов API Microsoft Graph из вашего веб-приложения на Node/Express.js.