重要
2025 年 5 月 1 日より、Azure AD B2C は新規のお客様向けに購入できなくなります。 詳細については、FAQ を参照してください。
この記事では、Azure Active Directory B2C (Azure AD B2C) 認証を独自のシングルページ アプリケーション (SPA) に追加する方法について説明します。 JavaScript 用 Microsoft Authentication Library (MSAL.js) を使用して SPA アプリケーションを作成する方法について説明します。
この記事を、サンプル SPA アプリケーションでの認証の構成に関する記事と共に使用し、サンプルの SPA アプリを独自の SPA アプリに置き換えてください。
概要
この記事では、Node.js と Express を使用して、基本的な Node.js Web アプリを作成します。 Express は、Web アプリケーションとモバイル アプリケーションに一連の機能を提供する、最小限かつ柔軟な Node.js Web アプリ フレームワークです。
MSAL.js 認証ライブラリは、SPA アプリへの認証と承認のサポートの追加を簡略化する Microsoft が提供するライブラリです。
ヒント
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パスです。 |
ブートストラップスタイルシート | Web開発をより迅速かつ簡単にするための無料のフロントエンドフレームワーク。 このフレームワークには、HTML ベースと CSS ベースのデザイン テンプレートが含まれています。 |
SPA インデックス ファイルをレンダリングするには、 myApp フォルダーに、次の HTML スニペットを含む index.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という名前の新しいファイルを作成します。
authConfig.js ファイルに次の JavaScript コードを追加します。
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>
をアプリ登録アプリケーション ID に置き換えます。 詳細については、「 サンプル SPA アプリケーションでの認証の構成」を参照してください。
ヒント
MSAL オブジェクト構成オプションの詳細については、 認証オプション に関する記事を参照してください。
手順 6: Azure AD B2C ユーザー フローを指定する
azure AD B2C 環境に関する情報を提供する policies.js ファイルを作成します。 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 Authentication Library (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 で取得されたアクセス トークンを要求の Authorization
HTTP ヘッダーに渡すように構成されます。
次のコードは、API エンドポイントへの HTTP GET
要求を定義し、 Authorization
HTTP ヘッダー内でアクセス トークンを渡します。 API の場所は、webApi
の キーで定義されます。
取得したトークンを使用して 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 要素を制御します。 たとえば、サインイン ボタンとサインアウト ボタンを表示し、ユーザーの ID トークン要求を画面にレンダリングします。
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 で認証を有効にする