Azure AD B2C を使用してサンプルのシングルページ アプリケーションで認証を構成する

この記事では、サンプルの JavaScript シングルページ アプリケーション (SPA) を使用して、Azure Active Directory B2C (Azure AD B2C) 認証を SPA に追加する方法について説明します。

概要

OpenID Connect (OIDC) は、OAuth 2.0 を基盤にした認証プロトコルです。 これを使用して、ユーザーはアプリケーションに安全にサインインできます。 この SPA のサンプルでは、MSAL.js と OIDC PKCE フローを使用します。 Microsoft が提供する MSAL.js ライブラリを使用すると、認証と認可のサポートを SPA アプリに簡単に追加できます。

サインイン フロー

サインイン フローでは、次の手順が実行されます。

  1. ユーザーが Web アプリにアクセスして [サインイン] を選択します。
  2. アプリによって認証要求が開始され、ユーザーが Azure AD B2C にリダイレクトされます。
  3. ユーザーがサインアップまたはサインインし、パスワードをリセットします。 ソーシャル アカウントを使用してサインインすることもできます。
  4. ユーザーがサインインすると、Azure AD B2C からアプリに認可コードが返されます。
  5. シングルページ アプリケーションで、ID トークンが検証され、要求が読み取られ、保護されたリソースと API をユーザーが呼び出すことが許可されます。

アプリ登録の概要

アプリで Azure AD B2C を使用してサインインし、Web API を呼び出せるようにするには、Azure AD B2C ディレクトリに 2 つのアプリケーションを登録します。

  • Web アプリケーションの登録により、アプリでは Azure AD B2C を使用してサインインできるようになります。 登録時に、"リダイレクト URI" を指定します。 リダイレクト URI は、Azure AD B2C での認証が完了した後にユーザーが Azure AD B2C によってリダイレクトされるエンドポイントです。 アプリの登録プロセスによって、アプリを一意に識別する "アプリケーション ID" ("クライアント ID" とも呼ばれます) が生成されます。

  • Web API の登録により、アプリではセキュリティで保護された Web API を呼び出すことができます。 登録には、Web API の "スコープ" が含まれます。 スコープを使用することで、Web API などの保護されたリソースへのアクセス許可を管理できます。 Web アプリケーションのアクセス許可を Web API のスコープに付与します。 アクセス トークンが要求されたら、アプリで必要なアクセス許可を要求の scope パラメーターに指定します。

アプリのアーキテクチャと登録について、次の図に示します。

Diagram of a web app with web API call registrations and tokens.

Web API の呼び出し

認証が完了した後、ユーザーがアプリと対話すると、保護された Web API が呼び出されます。 その Web API では、ベアラー トークン認証が使用されます。 ベアラー トークンは、アプリによって Azure AD B2C から取得されたアクセス トークンです。 アプリでは、HTTPS 要求の Authorization ヘッダーでトークンを渡します。

Authorization: Bearer <access token>

アクセス トークンのスコープが Web API のスコープと一致しない場合、認証ライブラリでは正しいスコープの新しいアクセス トークンが取得されます。

サインアウト フロー

サインアウト フローには、次の手順が含まれます。

  1. アプリから、ユーザーがサインアウトします。
  2. アプリによってそのセッション オブジェクトがクリアされ、認証ライブラリによってそのトークン キャッシュがクリアされます。
  3. アプリによってユーザーが Azure AD B2C サインアウト エンドポイントに移動し、Azure AD B2C セッションが終了されます。
  4. ユーザーが再びアプリにリダイレクトされます。

前提条件

次が実行されているコンピューター:

手順 1: ユーザー フローを構成する

ユーザーがアプリにサインインしようとすると、ユーザー フローを介した承認エンドポイントへの認証要求がアプリによって開始されます。 ユーザー フローによって、ユーザーのエクスペリエンスが定義および制御されます。 ユーザーがユーザー フローを完了すると、Azure AD B2C によってトークンが生成され、ユーザーはアプリケーションにリダイレクトされます。

ユーザー フローまたはカスタム ポリシーの作成をまだ行っていない場合は、作成します。 手順を繰り返して、次のように 3 つの個別のユーザー フローを作成します。

  • サインインと サインアップを結合したユーザー フロー (例:)susi。 このユーザー フローでは、パスワードを忘れた 場合のエクスペリエンスもサポート されています。
  • プロファイル編集 ユーザー フロー (例: edit_profile) 。
  • パスワードのリセット ユーザー フロー (例: reset_password)。

Azure AD B2C は、 B2C_1_ ユーザー フロー名の前に付加されます。 たとえば、susiB2C_1_susi になります。

手順 2: SPA と API を登録する

この手順では、SPA と Web API アプリケーションの登録を作成し、Web API のスコープを指定します。

手順 2.1: Web API アプリケーションを登録する

Web API アプリの登録 (App ID: 2) を作成するには、次の手順に従います。

  1. Azure portal にサインインします。

  2. ご自分の Azure AD B2C テナントが含まれるディレクトリを必ず使用してください。 ポータル ツールバーの [Directories + subscriptions](ディレクトリ + サブスクリプション) アイコンを選択します。

  3. [ポータルの設定] | [Directories + subscriptions](ディレクトリ + サブスクリプション) ページの [ディレクトリ名] の一覧で自分の Azure AD B2C ディレクトリを見つけて、 [切り替え] を選択します。

  4. Azure portal で、 [Azure AD B2C] を検索して選択します。

  5. [アプリの登録] を選択し、 [新規登録] を選択します。

  6. アプリケーションの名前を入力します (my-api1 など)。 [リダイレクト URI][サポートされているアカウントの種類] を既定値のままにします。

  7. [登録] を選択します。

  8. アプリ登録が完了したら、 [概要] を選択します。

  9. アプリケーション (クライアント) ID の値を記録しておきます。これは、後で Web アプリケーションを構成するときに使用します。

    Screenshot that demonstrates how to get a web A P I application I D.

手順 2.2: スコープを構成する

  1. 作成した my-api1 アプリケーション (App ID: 2) を選択して、その [概要] ページを開きます。

  2. [管理][API の公開] を選択します。

  3. [アプリケーション ID URI] の横にある [設定] リンクを選択します。 既定値 (GUID) を一意の名前 (例: tasks-api) に置き換え、[保存] を選択します。

    Web アプリケーションで Web API のアクセス トークンを要求するときに、API に対して定義する各スコープのプレフィックスとしてこの URI を追加する必要があります。

  4. [この API で定義されるスコープ] で、 [スコープの追加] を選択します。

  5. API への読み取りアクセスを定義するスコープを作成するには:

    1. [スコープ名]tasks.read を入力します。
    2. [管理者の同意の表示名] で、「Read access to tasks API」を入力します。
    3. [管理者の同意の説明] で、「Allows read access to the tasks API」を入力します。
  6. [スコープの追加] を選択します。

  7. [Add a scope (スコープの追加)] を選択し、API への書き込みアクセスを定義するスコープを追加します。

    1. [スコープ名] に「tasks.write」を入力します。
    2. [管理者の同意の表示名] に、「Write access to tasks API」を入力します。
    3. [管理者の同意の説明] に、「Allows write access to the tasks API」を入力します。
  8. [スコープの追加] を選択します。

手順 2.3: SPA を登録する

SPA の登録を作成するには、次の手順のようにします。

  1. Azure portal にサインインします。
  2. 複数のテナントにアクセスできる場合、上部のメニューの [設定] アイコンを選択し、[ディレクトリとサブスクリプション] メニューからお使いの Azure AD B2C テナントに切り替えます。
  3. Azure AD B2C を検索して選択します。
  4. [アプリの登録] を選択し、 [新規登録] を選択します。
  5. アプリケーションの [名前] を入力します (MyApp など)。
  6. [サポートされているアカウントの種類] で、 [Accounts in any identity provider or organizational directory (for authenticating users with user flows)]((ユーザー フローを使用してユーザーを認証するための) 任意の ID プロバイダーまたは組織のディレクトリのアカウント) を選択します。
  7. [リダイレクト URI] で、 [Single-page application (SPA)](シングルページ アプリケーション (SPA)) を選択し、URL ボックスに「http://localhost:6420」と入力します。
  8. [アクセス許可] で、 [Grant admin consent to openid and offline access permissions](OpenID とオフラインのアクセス許可に管理者の同意を与える) チェック ボックスをオンにします。
  9. [登録] を選択します。

アプリケーション (クライアント) ID を記録しておきます。これは、後で Web アプリケーションを構成するときに使用します。

Screenshot of the web app Overview page for recording your web application ID.

手順 2.4: 暗黙的な許可のフローを有効にする

お使いの環境で、SPA アプリにより MSAL.js 1.3 以前と暗黙の許可フローが使われている場合、またはユーザー フローやカスタム ポリシーをテストするために https://jwt.ms/ アプリを構成する場合は、アプリの登録で暗黙の許可フローを有効にする必要があります。

  1. 左側のメニューの [管理] セクションで、 [認証] を選択します。

  2. [暗黙的な許可およびハイブリッド フロー] で、[アクセス トークン (暗黙的なフローに使用)][ID トークン (暗黙的およびハイブリッド フローに使用)] の両方のチェック ボックスをオンにします。

  3. [保存] を選択します。

アプリで MSAL.js 2.0 以降を使っている場合、MSAL.js 2.0 以降では PKCE による承認コード フローがサポートされているため、暗黙的なフロー許可を有効にしないでください。 この記事の SPA アプリでは、PKCE フローを使います。そのため、暗黙的な許可フローを有効にする必要はありません。

手順 2.5: アクセス許可を付与する

アプリ (アプリ ID: 1) にアクセス許可を付与するには、次の手順をおこないます。

  1. [アプリの登録] を選択し、作成したアプリを選択します (アプリ ID: 1)。

  2. [管理] の下にある [API のアクセス許可] を選択します。

  3. [構成されたアクセス許可] の下で [アクセス許可の追加] を選択します。

  4. [自分の API] タブを選択します。

  5. Web アプリケーションへのアクセス許可が必要な API を選択します (アプリ ID: 2)。 たとえば、「my-api1」と入力します。

  6. [アクセス許可] で、 [タスク] を展開し、前に定義したスコープを選択します(たとえば、tasks.readtasks.write)。

  7. [アクセス許可の追加] を選択します.

  8. [<テナント名> に管理者の同意を与えます] を選択します。

  9. [はい] を選択します。

  10. [最新の情報に更新] を選択し、両方のスコープの [状態] に、Granted for ...(... に付与されました) と表示されていることを確認します。

  11. [Configured permissions (構成済みのアクセス許可)] の一覧からスコープを選択し、スコープの完全な名前をコピーします。

    Screenshot of the configured permissions pane, showing that read access permissions are granted.

手順 3: SPA のサンプル コードを取得する

このサンプルでは、シングルページ アプリケーションでユーザーのサインアップとサインインに Azure AD B2C を使用する方法を示します。 その後、アプリではアクセス トークンを取得して、保護された Web API を呼び出します。

SPA サンプル コードは、次のいずれかの方法で入手できます。

手順 3.1: SPA のサンプルを更新する

SPA サンプルを入手したら、Azure AD B2C と Web API の値でコードを更新します。 サンプル フォルダー内の App フォルダーにある、次の表の JavaScript ファイルを開き、それぞれの対応する値で更新します。

ファイル Key
authConfig.js clientId 手順 2.3 の SPA ID。
policies.js names ユーザー フロー、または手順 1 で作成したカスタム ポリシー。
policies.js authorities B2C Azure ADフローまたは カスタム ポリシー機関 ( https://<your-tenant-name>.b2clogin.com/<your-tenant-name>.onmicrosoft.com/<your-sign-in-sign-up-policy> など) 。 your-sign-in-sign-up-policy手順 1 で作成したユーザー フローまたはカスタム ポリシーに置き換えます
policies.js authorityDomain お使いの Azure AD B2C 機関ドメイン(<your-tenant-name>.b2clogin.comなど)です。
apiConfig.js b2cScopes 手順 2.2 で作成した Web API スコープ (例: b2cScopes: ["https://<your-tenant-name>.onmicrosoft.com/tasks-api/tasks.read"])。
apiConfig.js webApi Web API の URL (http://localhost:5000/hello)。

結果のコードは次のサンプルのようになります。

authConfig.js:

const msalConfig = {
    auth: {
      clientId: "<your-MyApp-application-ID>", // This is the ONLY mandatory field; everything else is optional.
      authority: b2cPolicies.authorities.signUpSignIn.authority, // Choose sign-up/sign-in user-flow as your default.
      knownAuthorities: [b2cPolicies.authorityDomain], // You must identify your tenant's domain as a known authority.
      redirectUri: "http://localhost:6420", // You must register this URI on Azure Portal/App Registration. Defaults to "window.location.href".
    },
    cache: {
      cacheLocation: "sessionStorage",  
      storeAuthStateInCookie: false, 
    },
    system: {
      loggerOptions: {
        loggerCallback: (level, message, containsPii) => {
          if (containsPii) {
            return;
          }
          switch (level) {
            case msal.LogLevel.Error:
              console.error(message);
              return;
            case msal.LogLevel.Info:
              console.info(message);
              return;
            case msal.LogLevel.Verbose:
              console.debug(message);
              return;
            case msal.LogLevel.Warning:
              console.warn(message);
              return;
          }
        }
      }
    }
  };
};

const loginRequest = {
  scopes: ["openid", ...apiConfig.b2cScopes],
};

const tokenRequest = {
  scopes: [...apiConfig.b2cScopes],  // e.g. ["https://fabrikamb2c.onmicrosoft.com/helloapi/demo.read"]
  forceRefresh: false // Set this to "true" to skip a cached token and go to the server to get a new token
};

policies.js:

const b2cPolicies = {
    names: {
        signUpSignIn: "b2c_1_susi",
        forgotPassword: "b2c_1_reset",
        editProfile: "b2c_1_edit_profile"
    },
    authorities: {
        signUpSignIn: {
            authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_susi",
        },
        forgotPassword: {
            authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_reset",
        },
        editProfile: {
            authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_edit_profile"
        }
    },
    authorityDomain: "your-tenant-name.b2clogin.com"
}

apiConfig.js:

const apiConfig = {
  b2cScopes: ["https://your-tenant-name.onmicrosoft.com/tasks-api/tasks.read"],
  webApi: "http://localhost:5000/hello"
};

手順 4: Web API のサンプル コードを取得する

Web API を登録し、そのスコープを定義したので、Azure AD B2C テナントで動作するように Web API コードを構成します。

Web API のサンプル コードは、次のいずれかの方法で入手できます。

手順 4.1: Web API を更新する

  1. コード エディターで config.json ファイルを開きます。

  2. 前に作成したアプリケーション登録で変数の値を変更します。 前提条件の一部として作成したユーザー フローを使用して、policyName を更新します (例: b2c_1_susi)。

    "credentials": {
        "tenantName": "<your-tenant-name>",
        "clientID": "<your-webapi-application-ID>"
    },
    "policies": {
        "policyName": "b2c_1_susi"
    },
    "resource": {
        "scope": ["tasks.read"] 
    },
    

手順 4.2: CORS を有効にする

シングルページ アプリケーションに Node.js Web API の呼び出しを許可するには、Web API で クロスオリジン リソース共有 (CORS) を有効にする必要があります。 運用アプリケーションでは、どのドメインによって要求が行われているかに注意してください。 この例では、どのドメインからの要求も許可します。

CORS を有効にするには、次のミドルウェアを使用します。 ダウンロードした Node.js の Web API コード サンプルでは、index.js ファイルに既に追加されています。

app.use((req, res, next) => {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Authorization, Origin, X-Requested-With, Content-Type, Accept");
    next();
});

手順 5: SPA と Web API を実行する

これで、シングルページ アプリケーションの API へのスコープ付きアクセスをテストする準備ができました。 ローカル マシンで Node.js Web API とサンプル JavaScript シングルページ アプリケーションの両方を実行します。 次に、シングルページ アプリケーションにサインインし、 [Call API](API の呼び出し) ボタンを選択して、保護されている API に対する要求を開始します。

Node.js Web API を実行する

  1. コンソール ウィンドウを開き、Node.js Web API サンプルが含まれるディレクトリに移動します。 次に例を示します。

    cd active-directory-b2c-javascript-nodejs-webapi
    
  2. 次のコマンドを実行します。

    npm install && npm update
    node index.js
    

    コンソール ウィンドウには、アプリケーションがホストされている場所のポート番号が表示されます。

    Listening on port 5000...
    

シングルページ アプリを実行する

  1. 別のコンソール ウィンドウを開き、JavaScript SPA サンプルが含まれるディレクトリに移動します。 次に例を示します。

    cd ms-identity-b2c-javascript-spa
    
  2. 次のコマンドを実行します。

    npm install && npm update
    npm start
    

    コンソール ウィンドウには、アプリケーションがホストされている場所のポート番号が表示されます。

    Listening on port 6420...
    
  3. アプリケーションを表示するには、ブラウザーで http://localhost:6420 にアクセスします。

    Screenshot of the SPA sample app displayed in the browser window.

  4. サインインアップまたはサインイン プロセスを完了します。 正常にログインすると、"User <ユーザー名> logged in" というメッセージが表示されます。

  5. [Call API] ボタンを選択します。 SPA では、要求内のアクセス トークンを保護された Web API に送信します。それによって、ログインしているユーザーの表示名が返されます。

    Screenshot of the SPA in a browser window, showing the username JSON result that's returned by the API.

アプリケーションをデプロイする

運用アプリケーションでは、アプリ登録のリダイレクト URI は通常、アプリが実行されているパブリックにアクセス可能なエンドポイント (https://contoso.com/signin-oidc など) です。

お使いの登録済みアプリケーションでは、いつでもリダイレクト URI を追加したり、変更したりすることができます。 リダイレクト URI には、次の制限があります。

  • 応答 URL は、スキーム https で始まる必要があります。
  • 応答 URL では大文字と小文字が区別されます。 大文字と小文字の区別は、実行中のアプリケーションの URL パスの場合と一致している必要があります。

次のステップ

この記事で説明した概念の詳細については、以下を参照してください。