這很重要
自 2025 年 5 月 1 日起,Azure AD B2C 將不再可供新客戶購買。 在我們的常見問題中深入瞭解。
本文說明如何將 Azure Active Directory B2C (Azure AD B2C) 驗證新增至您自己的單頁應用程式 (SPA)。 瞭解如何使用適用於 JavaScript 的 Microsoft 驗證連結庫建立 SPA 應用程式(MSAL.js)。
使用本文搭配 在範例 SPA 應用程式中設定驗證,並以您自己的 SPA 應用程式取代範例 SPA 應用程式。
概觀
本文使用 Node.js 和 Express 來建立基本 Node.js Web 應用程式。 Express 是一個最小而靈活的 Node.js Web 應用程式框架,它為 Web 和行動應用程式提供了一組功能。
MSAL.js 身份驗證庫是一個Microsoft提供的庫,可簡化向 SPA 應用程式添加身份驗證和授權支持的過程。
小提示
整個 MSAL.js 代碼在客戶端運行。 您可以將 Node.js 和 Express 伺服器端代碼替換為其他解決方案,例如 .NET Core、Java 和超文字預處理器 (PHP) 腳本語言。
先決條件
若要檢閱必要條件和整合指示,請參閱 在範例 SPA 應用程式中設定驗證。
步驟 1:建立 SPA 應用程式專案
您可以使用現有的 SPA 應用程式專案,或建立新的專案。 若要建立新的專案,請執行下列動作:
開啟命令殼層,並建立新的目錄(例如 myApp)。 此目錄將包含您的應用程式程式代碼、使用者介面和組態檔。
輸入您建立的目錄。
使用
npm init命令為您的應用程式建立package.json檔案。 此命令會提示您輸入有關應用程式的資訊(例如,應用程式的名稱和版本,以及初始入口點的名稱, 即index.js 檔案)。 執行下列命令,並接受預設值:
npm init
步驟 2:安裝相依性
若要安裝 Express 套件,請在命令殼層中執行下列命令:
npm install express
若要找出應用程式的靜態檔案,伺服器端程式代碼會使用 Path 套件。
若要安裝 Path 套件,請在命令殼層中執行下列命令:
npm install path
步驟 3:設定網頁伺服器
在 myApp 資料夾中,創建一個名為 index.js的檔,其中包含以下代碼:
// Initialize express
const express = require('express');
const app = express();
// The port to listen to incoming HTTP requests
const port = 6420;
// Initialize path
const path = require('path');
// Set the front-end folder to serve public assets.
app.use(express.static('App'));
// Set up a route for the index.html
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/index.html'));
});
// Start the server, and listen for HTTP requests
app.listen(port, () => {
console.log(`Listening on http://localhost:${port}`);
});
步驟 4:建立 SPA 使用者介面
新增 SPA 應用程式 index.html 檔案。 此檔案會實作以 Bootstrap 架構建置的使用者介面,並匯入組態、驗證和 Web API 呼叫的腳本檔案。
下表詳細介紹了 index.html 檔引用的資源:
| 參考文獻 | 定義 |
|---|---|
| MSAL.js 函式庫 | MSAL.js 身份驗證 JavaScript 庫 CDN 路徑。 |
| Bootstrap 樣式表單 | 免費的前端架構,可更快速且更輕鬆地進行 Web 開發。 架構包含 HTML 型和 CSS 型設計範本。 |
要呈現 SPA 索引檔,請在 myApp 資料夾中建立一個名為 index.html的檔,其中包含以下 HTML 代碼片段:
<!DOCTYPE html>
<html>
<head>
<title>My Azure AD B2C test app</title>
</head>
<body>
<h2>My Azure AD B2C test app</h2>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
<button type="button" id="signIn" class="btn btn-secondary" onclick="signIn()">Sign-in</button>
<button type="button" id="signOut" class="btn btn-success d-none" onclick="signOut()">Sign-out</button>
<h5 id="welcome-div" class="card-header text-center d-none"></h5>
<br />
<!-- Content -->
<div class="card">
<div class="card-body text-center">
<pre id="response" class="card-text"></pre>
<button type="button" id="callApiButton" class="btn btn-primary d-none" onclick="passTokenToApi()">Call API</button>
</div>
</div>
<script src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js" integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr" crossorigin="anonymous"></script>
<!-- Importing app scripts (load order is important) -->
<script type="text/javascript" src="./apiConfig.js"></script>
<script type="text/javascript" src="./policies.js"></script>
<script type="text/javascript" src="./authConfig.js"></script>
<script type="text/javascript" src="./ui.js"></script>
<!-- <script type="text/javascript" src="./authRedirect.js"></script> -->
<!-- uncomment the above line and comment the line below if you would like to use the redirect flow -->
<script type="text/javascript" src="./authRedirect.js"></script>
<script type="text/javascript" src="./api.js"></script>
</body>
</html>
步驟 5:設定驗證連結庫
配置 MSAL.js 庫與 Azure AD B2C 的整合方式。 MSAL.js 庫使用通用配置對象連接到 Azure AD B2C 租戶的身份驗證終結點。
若要設定驗證連結庫,請執行下列動作:
在 myApp 資料夾中,建立名為 App 的新資料夾。
在 App 資料夾中,創建名為 authConfig.js的新檔。
將以下 JavaScript 代碼新增到 authConfig.js 檔中:
const msalConfig = { auth: { clientId: "<Application-ID>", authority: b2cPolicies.authorities.signUpSignIn.authority, knownAuthorities: [b2cPolicies.authorityDomain], redirectUri: "http://localhost:6420", }, cache: { cacheLocation: "localStorage", . storeAuthStateInCookie: false, } }; const loginRequest = { scopes: ["openid", ...apiConfig.b2cScopes], }; const tokenRequest = { scopes: [...apiConfig.b2cScopes], forceRefresh: false };將取代
<Application-ID>為您的應用程式註冊應用程式識別碼。 如需詳細資訊,請參閱 在範例 SPA 應用程式中設定驗證。
小提示
如需更多 MSAL 物件組態選項,請參閱 驗證選項 一文。
步驟 6:指定您的 Azure AD B2C 使用者流程
創建 policies.js 檔,該檔提供有關 Azure AD B2C 環境的資訊。 MSAL.js 庫使用此資訊創建對 Azure AD B2C 的身份驗證請求。
若要指定您的 Azure AD B2C 使用者流程,請執行下列動作:
在 App 資料夾中,創建名為 policies.js的新檔。
將以下代碼新增到 policies.js 檔案中:
const b2cPolicies = { names: { signUpSignIn: "B2C_1_SUSI", editProfile: "B2C_1_EditProfile" }, authorities: { signUpSignIn: { authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-SignInOrSignUp-Policy-Id", }, editProfile: { authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-EditProfile-Policy-Id" } }, authorityDomain: "contoso.b2clogin.com" }將
B2C_1_SUSI取代為您的登入 Azure AD B2C 原則名稱。將
B2C_1_EditProfile替換為您的 Azure AD B2C 配置檔政策名稱。將所有
contoso的索引項替換為您的Azure AD B2C 租戶名稱。
步驟 7:使用 MSAL 登入使用者
在此步驟中,實作 方法來初始化登入流程、API 存取令牌擷取和註銷方法。
如需詳細資訊,請參閱 使用 Microsoft 驗證連結庫 (MSAL) 登入使用者 一文。
若要登入使用者,請執行下列動作:
在 App 資料夾中,創建名為 authRedirect.js的新檔。
在 authRedirect.js中,複製並粘貼以下代碼:
// Create the main myMSALObj instance // configuration parameters are located at authConfig.js const myMSALObj = new msal.PublicClientApplication(msalConfig); let accountId = ""; let idTokenObject = ""; let accessToken = null; myMSALObj.handleRedirectPromise() .then(response => { if (response) { /** * For the purpose of setting an active account for UI update, we want to consider only the auth response resulting * from SUSI flow. "tfp" claim in the id token tells us the policy (NOTE: legacy policies may use "acr" instead of "tfp"). * To learn more about B2C tokens, visit https://learn.microsoft.com/azure/active-directory-b2c/tokens-overview */ if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) { handleResponse(response); } } }) .catch(error => { console.log(error); }); function setAccount(account) { accountId = account.homeAccountId; idTokenObject = account.idTokenClaims; myClaims= JSON.stringify(idTokenObject); welcomeUser(myClaims); } function selectAccount() { /** * See here for more information on account retrieval: * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md */ const currentAccounts = myMSALObj.getAllAccounts(); if (currentAccounts.length < 1) { return; } else if (currentAccounts.length > 1) { /** * Due to the way MSAL caches account objects, the auth response from initiating a user-flow * is cached as a new account, which results in more than one account in the cache. Here we make * sure we are selecting the account with homeAccountId that contains the sign-up/sign-in user-flow, * as this is the default flow the user initially signed-in with. */ const accounts = currentAccounts.filter(account => account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase()) && account.idTokenClaims.iss.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase()) && account.idTokenClaims.aud === msalConfig.auth.clientId ); if (accounts.length > 1) { // localAccountId identifies the entity for which the token asserts information. if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) { // All accounts belong to the same user setAccount(accounts[0]); } else { // Multiple users detected. Logout all to be safe. signOut(); }; } else if (accounts.length === 1) { setAccount(accounts[0]); } } else if (currentAccounts.length === 1) { setAccount(currentAccounts[0]); } } // in case of page refresh selectAccount(); async function handleResponse(response) { if (response !== null) { setAccount(response.account); } else { selectAccount(); } } function signIn() { myMSALObj.loginRedirect(loginRequest); } function signOut() { const logoutRequest = { postLogoutRedirectUri: msalConfig.auth.redirectUri, }; myMSALObj.logoutRedirect(logoutRequest); } function getTokenRedirect(request) { request.account = myMSALObj.getAccountByHomeId(accountId); return myMSALObj.acquireTokenSilent(request) .then((response) => { // In case the response from B2C server has an empty accessToken field // throw an error to initiate token acquisition if (!response.accessToken || response.accessToken === "") { throw new msal.InteractionRequiredAuthError; } else { console.log("access_token acquired at: " + new Date().toString()); accessToken = response.accessToken; passTokenToApi(); } }).catch(error => { console.log("Silent token acquisition fails. Acquiring token using popup. \n", error); if (error instanceof msal.InteractionRequiredAuthError) { // fallback to interaction when silent call fails return myMSALObj.acquireTokenRedirect(request); } else { console.log(error); } }); } // Acquires and access token and then passes it to the API call function passTokenToApi() { if (!accessToken) { getTokenRedirect(tokenRequest); } else { try { callApi(apiConfig.webApi, accessToken); } catch(error) { console.log(error); } } } function editProfile() { const editProfileRequest = b2cPolicies.authorities.editProfile; editProfileRequest.loginHint = myMSALObj.getAccountByHomeId(accountId).username; myMSALObj.loginRedirect(editProfileRequest); }
步驟 8:設定 Web API 位置和範圍
若要允許 SPA 應用程式呼叫 Web API,請提供 Web API 端點位置,以及用來授權存取 Web API 的範圍 。
若要設定 Web API 位置和範圍,請執行下列動作:
在 App 資料夾中,創建名為 apiConfig.js的新檔。
在 apiConfig.js中,複製並粘貼以下代碼:
// The current application coordinates were pre-registered in a B2C tenant. const apiConfig = { b2cScopes: ["https://contoso.onmicrosoft.com/tasks/tasks.read"], webApi: "https://mydomain.azurewebsites.net/tasks" };將
contoso替換為您的租用戶名稱。 如設定範圍一文所述,可以找到所需的 範圍 名稱。將
webApi的值替換為您的 Web API 端點位置。
步驟 9:呼叫您的 Web API
定義 API 端點的 HTTP 要求。 HTTP 請求被配置為將使用 MSAL.js 獲得的訪問令牌傳遞到請求的 HTTP 標頭中。
下列程式代碼會定義對 API 端點的 HTTP GET 要求,並在 HTTP 標頭內 Authorization 傳遞存取令牌。 API 位置由 webApiapiConfig.js中的鍵定義。
若要使用您取得的令牌來呼叫 Web API,請執行下列動作:
在 App 資料夾中,創建名為 api.js的新檔。
將以下代碼新增到 api.js 檔案中:
function callApi(endpoint, token) { const headers = new Headers(); const bearer = `Bearer ${token}`; headers.append("Authorization", bearer); const options = { method: "GET", headers: headers }; logMessage('Calling web API...'); fetch(endpoint, options) .then(response => response.json()) .then(response => { if (response) { logMessage('Web API responded: ' + response.name); } return response; }).catch(error => { console.error(error); }); }
步驟 10:新增 UI 元素參考
SPA 應用程式會使用 JavaScript 來控制 UI 元素。 例如,它會顯示登入和註銷按鈕,並將使用者的標識元令牌宣告轉譯到畫面。
若要新增 UI 元素參考,請執行下列動作:
在 App 資料夾中,創建名為 ui.js的檔案。
將以下代碼新增到 ui.js 檔案中:
// Select DOM elements to work with const signInButton = document.getElementById('signIn'); const signOutButton = document.getElementById('signOut') const titleDiv = document.getElementById('title-div'); const welcomeDiv = document.getElementById('welcome-div'); const tableDiv = document.getElementById('table-div'); const tableBody = document.getElementById('table-body-div'); const editProfileButton = document.getElementById('editProfileButton'); const callApiButton = document.getElementById('callApiButton'); const response = document.getElementById("response"); const label = document.getElementById('label'); function welcomeUser(claims) { welcomeDiv.innerHTML = `Token claims: </br></br> ${claims}!` signInButton.classList.add('d-none'); signOutButton.classList.remove('d-none'); welcomeDiv.classList.remove('d-none'); callApiButton.classList.remove('d-none'); } function logMessage(s) { response.appendChild(document.createTextNode('\n' + s + '\n')); }
步驟 11:執行 SPA 應用程式
在您的命令殼層中,執行下列命令:
npm install
npm ./index.js
- 移至 https://localhost:6420.
- 選取 [登入]。
- 完成註冊或登入程式。
成功驗證之後,已剖析的 ID 令牌會顯示在畫面上。 選取 Call API 以呼叫您的 API 端點。
後續步驟
- 深入瞭解程式 代碼範例。
- 使用 Azure AD B2C 在您自己的 SPA 應用程式中設定驗證選項。
- 在您自己的 Web API 中啟用驗證。