次の方法で共有


Azure AD B2C を使用して独自のシングルページ アプリケーションで認証を有効にするEnable authentication in your own single-page application by using Azure AD B2C

重要

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アプリプロジェクトを使用することも、新しいプロジェクトを作成することもできます。 新しいプロジェクトを作成するには、次の操作を行います。

  1. コマンド シェルを開き、新しいディレクトリ ( myApp など) を作成します。 このディレクトリには、アプリ コード、ユーザー インターフェイス、および構成ファイルが含まれます。

  2. 作成したディレクトリを入力します。

  3. 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 テナントの認証エンドポイントに接続します。

認証ライブラリを設定するには、次の手順を実行します。

  1. myApp フォルダーに、App という名前の新しいフォルダーを作成します。

  2. App フォルダー内に、authConfig.jsという名前の新しいファイルを作成します。

  3. 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
    };
    
  4. <Application-ID> をアプリ登録アプリケーション ID に置き換えます。 詳細については、「 サンプル SPA アプリケーションでの認証の構成」を参照してください。

ヒント

MSAL オブジェクト構成オプションの詳細については、 認証オプション に関する記事を参照してください。

手順 6: Azure AD B2C ユーザー フローを指定する

azure AD B2C 環境に関する情報を提供する policies.js ファイルを作成します。 MSAL.js ライブラリでは、この情報を使用して Azure AD B2C への認証要求を作成します。

Azure AD B2C ユーザー フローを指定するには、次の操作を行います。

  1. App フォルダー内に、policies.jsという名前の新しいファイルを作成します。

  2. 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"
    }
    
  3. B2C_1_SUSI をサインイン Azure AD B2C ポリシー名に置き換えます。

  4. B2C_1_EditProfile を編集プロファイルの Azure AD B2C ポリシー名に置き換えます。

  5. contoso のすべてのインスタンスを Azure AD B2C テナント名に置き換えます。

手順 7: MSAL を使用してユーザーをサインインさせる

この手順では、サインイン フローを初期化するメソッド、API アクセス トークンの取得メソッド、およびサインアウト メソッドを実装します。

詳細については、 Microsoft Authentication Library (MSAL) を使用してユーザーにサインインする 記事を参照してください。

ユーザーをサインインさせるには、次の手順を実行します。

  1. App フォルダー内に、authRedirect.jsという名前の新しいファイルを作成します。

  2. 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 の場所とスコープを構成するには、次の操作を行います。

  1. App フォルダー内に、apiConfig.jsという名前の新しいファイルを作成します。

  2. 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"
    };
    
  3. contoso を、実際のテナント名に置き換えます。 必要なスコープ名は、「 スコープの構成 」の記事で説明されているとおりにあります。

  4. webApi の値を Web API エンドポイントの場所に置き換えます。

手順 9: Web API を呼び出す

API エンドポイントへの HTTP 要求を定義します。 HTTP 要求は、 MSAL.js で取得されたアクセス トークンを要求の Authorization HTTP ヘッダーに渡すように構成されます。

次のコードは、API エンドポイントへの HTTP GET 要求を定義し、 Authorization HTTP ヘッダー内でアクセス トークンを渡します。 API の場所は、webApi キーで定義されます。

取得したトークンを使用して Web API を呼び出すには、次の操作を行います。

  1. App フォルダー内に、api.jsという名前の新しいファイルを作成します。

  2. 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 要素参照を追加するには、次の操作を行います。

  1. App フォルダー内に、ui.jsという名前のファイルを作成します。

  2. 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
  1. https://localhost:6420. に移動します。
  2. [サインイン] を選択します。
  3. サインインアップまたはサインイン プロセスを完了します。

認証に成功すると、解析された ID トークンが画面に表示されます。 [ Call API ] を選択して API エンドポイントを呼び出します。

次のステップ