教學:將認證從 App Service 傳遞至後端 API 並連接至 Microsoft Graph

學習如何建立並設定後端 App Service 應用程式,讓它接受前端應用程式的使用者憑證,然後將該憑證交換成下游的 Azure 服務。 此方法允許使用者登入前端 App Service 應用程式,將憑證傳至後端 App Service,然後存取具有相同身份的 Azure 服務。

在本教學課程中,您將瞭解如何:

  • 設定後端驗證應用程式,以提供範圍限定為下游 Azure 服務的權杖。
  • 使用 JavaScript 程式代碼來交換已 登入使用者的存取令牌 ,以取得下游服務的新令牌。
  • 使用 JavaScript 程式代碼來存取下游服務。

先決條件

在開始此教學前,請先完成前一個教學「從一個安全的 JavaScript 應用程式存取 Microsoft Graph」,再開始這個教學。 不要在教學結束時移除資源。 這個教學假設你有兩個應用程式服務及其對應的認證應用程式。

上一個教學使用 Azure Cloud Shell 作為 Azure CLI 的 Shell。 本教學課程會繼續使用該用法。

建築

教學展示了如何將前端應用程式提供的使用者憑證傳給後端應用程式,再傳到 Azure 服務。 在本教學課程中,下游服務會是 Microsoft Graph。 用戶的登入憑證可用來從 Microsoft Graph 取得其個人資料。

App Service 代表已登入的使用者,連線到 App Service,再連線到 Microsoft Graph 的架構圖。

使用者在此架構中取得Microsoft Graph 資訊的驗證流程:

之前的教學涵蓋了:

  1. 讓使用者登入已設定為使用 Active Directory 作為識別提供者的前端應用程式。
  2. 前端應用程式服務將使用者的代幣傳遞給後端應用程式服務。
  3. 後端應用程式被保護,允許前端提出 API 請求。 使用者的存取權杖具有後端 API 的對象,以及 user_impersonation 的範圍。
  4. 後端應用程式註冊已具有 Microsoft Graph,且範圍為 User.Read。 此範圍預設會加到所有應用程式註冊中。
  5. 在上一個教學課程結束時,由於尚未連線 Graph,因此會將假的設定檔傳回前端應用程式。

本教學課程會擴充架構:

  1. 授與系統管理員同意,以略過後端應用程式的使用者同意畫面。
  2. 變更應用程式程式代碼,將從前端應用程式傳送的存取令牌轉換為具有 Microsoft Graph 所需許可權的存取令牌。
  3. 提供程式碼,讓後端應用程式將權杖交換成具有下游 Azure 服務 (例如 Microsoft Graph) 範圍的新權杖。
  4. 提供程式碼讓後端應用程式 使用新令牌 ,以目前已認證的使用者身份存取下游服務。
  5. 使用 az webapp deploy 重新部署後端應用程式。
  6. 在本教學課程結束時,由於已連線 Graph,因此會將真的設定檔傳回前端應用程式。

本教學課程不會:

  • 調整前端應用程式以符合之前教學的要求。
  • 更改後端認證應用程式的範圍權限,因為 User.Read 預設會加到所有認證應用程式中。

在之前的教學中,當使用者登入前端應用程式時,會跳出一個視窗詢問使用者同意。

在本教學課程中,若要從 Microsoft Graph 讀取使用者配置檔,後端應用程式必須以 Microsoft Graph 的必要許可權交換已登入使用者的存取令牌,以取得新的 存取令牌 。 由於使用者未直接連接後端應用程式,無法互動式存取同意畫面。 你必須透過在 Microsoft Entra ID 中設定後端應用程式註冊,以授與 系統管理員同意來繞過這個問題。 通常由 Microsoft Entra 管理員來更改這個設定。

  1. 打開 Azure 入口網站,搜尋你後端 App Service 的資源。

  2. 找到 設定>認證 區塊。

  3. 選取識別提供者以移至驗證應用程式。

  4. 在驗證應用程式中,選擇 管理>API 權限。

  5. 選取 [針對預設目錄授與管理員同意]。

    Azure 入口驗證應用程式的截圖,管理員同意按鈕被高亮。

  6. 在彈出視窗中,選取 [ 是 ] 以確認同意。

  7. 確認 [狀態] 欄顯示 [已授與預設目錄]。 使用此設定時,不再需要後端應用程式向登入的用戶顯示同意畫面,而且可以直接要求存取令牌。 登入的使用者具有範圍設定的 User.Read 存取權,因為這是建立應用程式註冊的預設範圍。

    狀態欄中已授予管理員同意的 Azure 入口驗證應用程式截圖。

2.安裝 npm 套件

在之前的教學中,後端應用程式不需要任何 npm 套件來進行認證,因為唯一的認證是透過在 Azure 入口網站設定身份提供者來完成。 在此教學中,必須將已登入使用者的後端 API 存取令牌交換為具有 Microsoft Graph 範圍的存取令牌。 這個交換是用兩個函式庫完成的,因為這個交換不再使用 App Service 認證。 相反地,它直接使用 Microsoft Entra ID 和 MSAL.js。

  1. 打開 Azure Cloud Shell,並切換到範例目錄的後端應用程式:

    cd js-e2e-web-app-easy-auth-app-to-app/backend
    
  2. 安裝 Azure Microsoft 認證函式庫(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. 在同一個檔案中,取消註解 getGraphProfile 路由中的下列 get-profile 行,以從 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. 將更新後的後端應用程式打包,不含本地相依套件與環境檔案:

    zip -r ../backend-graph.zip . -x '*/node_modules/*' '*/.git/*' '*/.env*'
    
  7. 部署更新後的套件:

    az webapp deploy --resource-group myAuthResourceGroup --name <back-end-app-name> --src-path ../backend-graph.zip --type zip
    

4. 檢查後端程式碼以交換後端 API 令牌與 Microsoft Graph 令牌

為了將後端 API 對象權杖變更為 Microsoft Graph 權杖,後端應用程式必須尋找租用戶識別碼,並將其用作 MSAL.js 設定物件的一部分。 由於後端應用程式是以 Microsoft 作為身份提供者設定,租戶 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 環境變數取得目前的租用戶標識碼。 可以使用正則表達式從變數中解析出識別碼。

檢查使用 MSAL.js 取得 Graph 權杖的程式碼

  1. 在 ./backend/src/with-graph/auth.js 檔案中檢視 getGraphToken() 函式。

  2. 建立 MSAL.js 設定物件。 使用 MSAL 設定來建立 clientCredentialAuthority. 設定代表要求。 然後使用 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 範圍,設定 Active Directory 應用程式註冊,使其具有下游服務 Microsoft Graph 的 API 權限。
  • 授與系統管理員同意,以略過後端應用程式的使用者同意畫面。
  • 變更應用程式程式代碼,將從前端應用程式傳送的存取令牌轉換為具有下游服務所需許可權的存取令牌,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. 在瀏覽器中使用前端網站。 如果令牌過期了,你可能需要刷新。

  2. 選取 Get user's profile。 這會將承載令牌中的認證傳輸到後端。

  3. 後端會回應您帳戶的真實 Microsoft Graph 設定檔。

    網頁瀏覽器的螢幕擷取畫面,顯示從後端應用程式成功取得真實設定檔後的前端應用程式。

7. 清理

在上述步驟中,您已建立資源群組中的 Azure 資源。

  1. 要刪除資源群組,請在 Cloud Shell 執行以下指令。 執行此命令約需一分鐘。

    az group delete --name myAuthResourceGroup
    
  2. 使用你之前在後端和前端應用程式區塊中找到並記錄的客戶 ID。

  3. 刪除前端和後端應用程式的註冊。

    # delete app - do this for both front-end and back-end client ids
    az ad app delete --id <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. 使用下游用戶端來存取服務功能。