チュートリアル: バックエンド API を介して App Service から Microsoft Graph への認証のフローを行う

フロントエンド アプリのユーザー資格情報を受け入れるようにバックエンド アプリ サービスを作成して構成した後、その資格情報をダウンストリームの Azure サービスと交換する方法について説明します。 これにより、ユーザーはフロントエンドの App Service にサインインし、その資格情報をバックエンドの App Service に渡してから、同じ ID で Azure サービスにアクセスできます。

このチュートリアルでは、以下の内容を学習します。

  • ダウンストリームの Azure サービスにスコープが設定されたトークンを提供するように、バックエンド認証アプリを構成する
  • JavaScript コードを使って、サインインしているユーザーのアクセス トークンを、ダウンストリームのサービスの新しいトークンと交換します。
  • JavaScript コードを使ってダウンストリームのサービスにアクセスします。

前提条件

このチュートリアルを始める前に、前の「チュートリアル: セキュリティで保護された JavaScript アプリからユーザーとして Microsoft Graph にアクセスする」を済ませ、ただしそのチュートリアルの最後でリソースを削除しないでください。 このチュートリアルでは、2 つのアプリ サービスとそれに対応する認証アプリがあることを前提としています。

前のチュートリアルでは、Azure CLI のシェルとして Azure Cloud Shell を使いました。 このチュートリアルでは、引き続きそれを使います。

アーキテクチャ

このチュートリアルでは、フロントエンド アプリによって提供されたユーザー資格情報をバックエンド アプリに渡し、さらに Azure サービスにそれを渡す方法について説明します。 このチュートリアルでのダウンストリーム サービスは Microsoft Graph です。 ユーザーの資格情報は、Microsoft Graph からプロファイルを取得するために使われます。

Architectural image of App Service connecting to App Service connecting to Microsoft Graph on behalf of a signed-in user.

このアーキテクチャでユーザーが Microsoft Graph の情報を取得するための認証フロー:

前のチュートリアルで説明した内容:

  1. ID プロバイダーとして Active Directory を使うように構成されたフロントエンド App Service にユーザーをサインインさせます。
  2. フロントエンド App Service は、ユーザーのトークンをバックエンド App Service に渡します。
  3. バックエンド アプリは、フロントエンドが API 要求を行えるようにセキュリティで保護されています。 ユーザーのアクセス トークンには、バックエンド API の対象ユーザーと、user_impersonation のスコープが設定されています。
  4. バックエンド アプリの登録には、スコープを User.Read とする Microsoft Graph が既に含まれます。 これは、すべてのアプリ登録に既定で追加されます。
  5. 前のチュートリアルの最後では、Graph が接続されていないため、"偽" のプロファイルがフロントエンド アプリに返されました。

このチュートリアルでは、そのアーキテクチャを拡張します。

  1. バックエンド アプリのユーザーの同意画面をバイパスするため、管理者の同意を付与します。
  2. フロントエンド アプリから送信されたアクセス トークンを、Microsoft Graph に必要なアクセス許可を持つアクセス トークンに変換するよう、アプリケーションのコードを変更します。
  3. バックエンド アプリに Microsoft Graph などのダウンストリーム Azure サービスのスコープを持つ新しいトークンとトークンを交換させるコードを提供します。
  4. バックエンド アプリに新しいトークンを使用して現在の認証ユーザーとしてダウンストリーム サービスにアクセスさせるコードを提供します。
  5. az webapp up を使ってバックエンド アプリを再デプロイします。
  6. このチュートリアルの最後では、Graph が接続されているため、"実際" のプロファイルがフロントエンド アプリに返されます。

このチュートリアルでは、次のことは行いません。

  • 前のチュートリアルからフロントエンド アプリを変更する。
  • User.Read が既定ですべての認証アプリに追加されるため、バックエンド認証アプリのスコープのアクセス許可を変更する。

前のチュートリアルでは、ユーザーがフロントエンド アプリにサインインすると、ユーザーの同意を求めるポップアップが表示されました。

このチュートリアルでは、Microsoft Graph からユーザー プロファイルを読み取るため、バックエンド アプリでは、サインインしているユーザーのアクセス トークンを、Microsoft Graph に対する必要なアクセス許可を持つ新しいアクセス トークンと、交換する必要があります。 ユーザーは、バックエンド アプリに直接接続されていないため、対話形式で同意画面にアクセスすることはできません。 これを回避するには、管理者の同意を付与するように、Microsoft Entra ID でバックエンド アプリのアプリ登録を構成する必要があります。 通常、この設定変更は Active Directory 管理者が行います。

  1. Azure portal を開き、バックエンド App Service の調査を検索します。

  2. [設定] -> [認証] セクションを見つけます。

  3. 認証アプリにアクセスするための ID プロバイダーを選びます。

  4. 認証アプリで、[管理] -> [API のアクセス許可] を選びます。

  5. [既定のディレクトリに管理者の同意を与えます] を選択します。

    Screenshot of Azure portal authentication app with admin consent button highlighted.

  6. ポップアップ ウィンドウで、[はい] を選んで同意を確認します。

  7. [状態] 列に [Granted for Default Directory] (既定のディレクトリに付与) と表示されていることを確認します。 この設定では、バックエンド アプリはサインインしているユーザーに同意画面を表示する必要がなく、アクセス トークンを直接要求できます。 アプリ登録が作成される既定のスコープは User.Read であるため、サインインしているユーザーはそのスコープ設定にアクセスできます。

    Screenshot of Azure portal authentication app with admin consent granted in status column.

2. npm パッケージをインストールする

前のチュートリアルでは、Azure portal で ID プロバイダーを構成することによってのみ認証が提供されたため、バックエンド アプリでは認証に npm パッケージは必要ありませんでした。 このチュートリアルでは、サインインしているユーザーのバックエンド API 用のアクセス トークンを、スコープ内の Microsoft Graph でのアクセス トークンと交換する必要があります。 この交換では、App Service 認証は使われなくなりますが、Microsoft Entra ID と MSAL.js を直接使うため、この交換は 2 つのライブラリで行われます。

  1. Azure Cloud Shell を開き、サンプル ディレクトリのバックエンド アプリに移動します。

    cd js-e2e-web-app-easy-auth-app-to-app/backend
    
  2. Azure MSAL の npm パッケージをインストールします。

    npm install @azure/msal-node
    
  3. Microsoft Graph の npm パッケージをインストールします。

    npm install @microsoft/microsoft-graph-client
    

3. 現在のトークンを Microsoft Graph のトークンと交換するコードを追加する

このステップを完了するためのソース コードは用意されています。 次の手順を使ってそれを組み込みます。

  1. ./src/server.js ファイルを開きます。

  2. ファイルの先頭にある次の依存関係のコメントを解除します。

    import { getGraphProfile } from './with-graph/graph';
    
  3. 同じファイルで、graphProfile 変数のコメントを解除します。

    let graphProfile={};
    
  4. 同じファイルで、get-profile ルート内の次の getGraphProfile 行のコメントを解除して、Microsoft Graph からプロファイルを取得します。

    // where did the profile come from
    profileFromGraph=true;
    
    // get the profile from Microsoft Graph
    graphProfile = await getGraphProfile(accessToken);
    
    // log the profile for debugging
    console.log(`profile: ${JSON.stringify(graphProfile)}`);
    
  5. 変更を保存します: Ctrl + s キー。

  6. バックエンド アプリを再デプロイします。

    az webapp up --resource-group myAuthResourceGroup --name <back-end-app-name> 
    
    

4. バックエンド API のトークンを Microsoft Graph のトークンと交換するバックエンド コードを調べる

バックエンド API の対象ユーザー トークンを Microsoft Graph のトークンに変更するには、バックエンド アプリでテナント ID を検索し、MSAL.js 構成オブジェクトの一部としてそれを使う必要があります。 バックエンド アプリは Microsoft を ID プロバイダーとして構成されているので、テナント ID と他のいくつかの必要な値は App Service のアプリ設定に既に存在します。

次のコードは、サンプル アプリで既に提供されています。 開発者は、この同じ機能を必要とする他のアプリの構築に、この作業を適用できるよう、それがそこにある理由と、その動作方法を理解しておく必要があります。

テナント ID を取得するためのコードを調べる

  1. ./backend/src/with-graph/auth.js ファイルを開きます。

  2. getTenantId() 関数を確認します。

    export function getTenantId() {
    
        const openIdIssuer = process.env.WEBSITE_AUTH_OPENID_ISSUER;
        const backendAppTenantId = openIdIssuer.replace(/https:\/\/sts\.windows\.net\/(.{1,36})\/v2\.0/gm, '$1');
    
        return backendAppTenantId;
    }
    
  3. この関数は、WEBSITE_AUTH_OPENID_ISSUER 環境変数から現在のテナント ID を取得します。 ID は、正規表現を使って変数から解析されます。

MSAL.js を使って Graph トークンを取得するコードを調べる

  1. 引き続き ./backend/src/with-graph/auth.js ファイルで、getGraphToken() 関数を確認します。

  2. MSAL.js 構成オブジェクトをビルドし、MSAL の構成を使って clientCredentialAuthority を作成します。 On-behalf-off 要求を構成します。 次に、acquireTokenOnBehalfOf を使って、バックエンド API のアクセス トークンを Graph のアクセス トークンと交換します。

    // ./backend/src/auth.js
    // Exchange current bearerToken for Graph API token
    // Env vars were set by App Service
    export async function getGraphToken(backEndAccessToken) {
    
        const config = {
            // MSAL configuration
            auth: {
                // the backend's authentication CLIENT ID 
                clientId: process.env.WEBSITE_AUTH_CLIENT_ID,
                // the backend's authentication CLIENT SECRET 
                clientSecret: process.env.MICROSOFT_PROVIDER_AUTHENTICATION_SECRET,
                // OAuth 2.0 authorization endpoint (v2)
                // should be: https://login.microsoftonline.com/BACKEND-TENANT-ID
                authority: `https://login.microsoftonline.com/${getTenantId()}`
            },
            // used for debugging
            system: {
                loggerOptions: {
                    loggerCallback(loglevel, message, containsPii) {
                        console.log(message);
                    },
                    piiLoggingEnabled: true,
                    logLevel: MSAL.LogLevel.Verbose,
                }
            }
        };
    
        const clientCredentialAuthority = new MSAL.ConfidentialClientApplication(config);
    
        const oboRequest = {
            oboAssertion: backEndAccessToken,
            // this scope must already exist on the backend authentication app registration 
            // and visible in resources.azure.com backend app auth config
            scopes: ["https://graph.microsoft.com/.default"]
        }
    
        // This example has App service validate token in runtime
        // from headers that can't be set externally
    
        // If you aren't using App service's authentication, 
        // you must validate your access token yourself
        // before calling this code
        try {
            const { accessToken } = await clientCredentialAuthority.acquireTokenOnBehalfOf(oboRequest);
            return accessToken;
        } catch (error) {
            console.log(`getGraphToken:error.type = ${error.type}  ${error.message}`);
        }
    }
    

5. 新しいトークンで Microsoft Graph にアクセスするためのバックエンド コードを調べる

フロントエンド アプリケーションにサインインしたユーザーとして Microsoft Graph にアクセスするための変更には、次のものが含まれます。

  • 必要なスコープ User.Read でダウンストリーム サービス Microsoft Graph への API アクセス許可を持つ Active Directory アプリ登録の構成。
  • バックエンド アプリのユーザーの同意画面をバイパスするため、管理者の同意を付与します。
  • フロントエンド アプリから送信されたアクセス トークンを、ダウンストリーム サービス Microsoft Graph に必要なアクセス許可を持つアクセス トークンに変換するよう、アプリケーションのコードを変更します。

コードに Microsoft Graph の正しいトークンが設定されたので、それを使って Microsoft Graph に対するクライアントを作成し、ユーザーのプロファイルを取得します。

  1. ./backend/src/graph.js を開きます

  2. getGraphProfile() 関数で、トークンを取得し、トークンから認証されたクライアントを取得した後、プロファイルを取得します。

    // 
    import graph from "@microsoft/microsoft-graph-client";
    import { getGraphToken } from "./auth.js";
    
    // Create client from token with Graph API scope
    export function getAuthenticatedClient(accessToken) {
        const client = graph.Client.init({
            authProvider: (done) => {
                done(null, accessToken);
            }
        });
    
        return client;
    }
    export async function getGraphProfile(accessToken) {
        // exchange current backend token for token with 
        // graph api scope
        const graphToken = await getGraphToken(accessToken);
    
        // use graph token to get Graph client
        const graphClient = getAuthenticatedClient(graphToken);
    
        // get profile of user
        const profile = await graphClient
            .api('/me')
            .get();
    
        return profile;
    }
    

6. 変更をテストする

  1. ブラウザーでフロントエンド Web サイトを使います。 URL は https://<front-end-app-name>.azurewebsites.net/ という形式です。 トークンの有効期限が切れている場合は、更新が必要になることがあります。

  2. [Get user's profile] を選択します。 これにより、ベアラー トークンの認証がバックエンドに渡されます。

  3. バックエンドは、アカウントの "実際" の Microsoft Graph プロファイルで応答します。

    Screenshot of web browser showing frontend application after successfully getting real profile from backend app.

7. クリーンアップする

前の手順では、リソース グループ内に Azure リソースを作成しました。

  1. Cloud Shell で次のコマンドを実行して、リソース グループを削除します。 このコマンドの実行には、少し時間がかかる場合があります。

    az group delete --name myAuthResourceGroup
    
  2. 認証アプリのクライアント ID を使います。これは、バックエンド アプリとフロントエンド アプリの Enable authentication and authorization セクションで以前に確認し、メモしておいたものです。

  3. フロントエンド アプリとバックエンド アプリの両方のアプリ登録を削除します。

    # delete app - do this for both frontend and backend client ids
    az ad app delete <client-id>
    

よく寄せられる質問

エラー 80049217 が発生しました。どういう意味ですか?

このエラー CompactToken parsing failed with error code: 80049217 は、バックエンド App Service が Microsoft Graph のトークンを返す権限を持たないことを意味します。 このエラーは、アプリの登録に User.Read アクセス許可がないために発生します。

エラー AADSTS65001 が発生しました。どういう意味ですか?

このエラー AADSTS65001: The user or administrator has not consented to use the application with ID \<backend-authentication-id>. Send an interactive authorization request for this user and resource は、バックエンド認証アプリが管理者の同意用に構成されていないことを意味します。 エラーはバックエンド アプリのログで示されるため、フロントエンド アプリケーションは、フロントエンド アプリに自分のプロファイルが表示されなかった理由をユーザーに伝えることができません。

ユーザーとして別のダウンストリーム Azure サービスに接続するにはどうすればよいですか?

このチュートリアルでは、Microsoft Graph に対して認証された API アプリを示していますが、同じ一般的な手順を適用して、ユーザーの代わりに任意の Azure サービスにアクセスできます。

  1. フロントエンド アプリケーションに変更はありません。 バックエンドの認証アプリの登録とバックエンド アプリのソース コードを変更するだけです。
  2. バックエンド API をスコープとするユーザーのトークンを、アクセスしたいダウンストリーム サービスへのトークンと交換します。
  3. ダウンストリーム サービスの SDK でトークンを使って、クライアントを作成します。
  4. ダウンストリームのクライアントを使って、サービスの機能にアクセスします。

次の手順