教程:将身份验证从应用服务通过后段 API 流入 Microsoft Graph

了解如何创建和配置后端应用服务以接受前端应用的用户凭据,然后将该凭据交换为下游 Azure 服务。 这允许用户登录到前端应用服务,将其凭据传递给后端应用服务,然后使用相同的标识访问 Azure 服务。

在本教程中,你将了解如何执行以下操作:

  • 配置后端身份验证应用以提供适用于下游 Azure 服务的令牌
  • 使用 JavaScript 代码将已登录用户的访问令牌交换为下游服务的新令牌。
  • 使用 JavaScript 代码访问下游服务。

先决条件

在开始本教程之前,请完成上一教程:以用户身份从受保护的 JavaScript 应用访问 Microsoft Graph,但不要在教程结束时删除资源。 本教程假定你有两个应用服务及其相应的身份验证应用。

上一教程使用 Azure Cloud Shell 作为 Azure CLI 的 shell。 本教程将继续使用该用法。

体系结构

本教程演示如何将前端应用提供的用户凭据传递到后端应用,然后传递到 Azure 服务。 在本教程中,下游服务为 Microsoft Graph。 用户的凭据用于从 Microsoft Graph 获取其配置文件。

应用服务代表已登录用户连接到 Microsoft Graph 的体系结构图。

用户在此体系结构中获取 Microsoft Graph 信息的身份验证流

之前的教程涵盖了:

  1. 将用户登录到配置为使用 Active Directory 作为标识提供者的前端应用服务。
  2. 前端应用服务将用户的令牌传递给后端应用服务。
  3. 后端应用受到保护,允许前端发出 API 请求。 用户的访问令牌具有后端 API 的受众和范围 user_impersonation
  4. 后端应用注册已具有范围为 User.Read 的 Microsoft Graph。 默认情况下,这会添加到所有应用注册中。
  5. 在上一教程结束时,由于 Graph 未连接,虚假的配置文件被返回到了前端应用。

本教程对体系结构进行了扩展:

  1. 授予管理员许可,让后端应用绕过用户同意屏幕。
  2. 更改应用程序代码,将从前端应用发送的访问令牌转换为具有 Microsoft Graph 所需权限的访问令牌。
  3. 提供代码,让后端应用交换令牌,获取具有下游 Azure 服务(例如 Microsoft Graph)范围的新令牌。
  4. 提供代码,让后端应用以当前身份验证用户的身份使用新令牌访问下游服务。
  5. 使用 az webapp up重新部署后端应用。
  6. 在本教程结束时,由于已连接 Graph,真实配置文件会返回到前端应用。

本教程不会:

  • 更改之前教程中的前端应用。
  • 更改后端身份验证应用的范围权限,因为 User.Read 默认情况下会添加到所有身份验证应用。

在之前的教程中,当用户登录到前端应用时,将显示一个弹出窗口,要求用户同意。

在本教程中,为了从 Microsoft Graph 读取用户配置文件,后端应用需要将已登录用户的访问令牌交换为具有 Microsoft Graph 所需权限的新访问令牌。 由于用户未直接连接到后端应用,他们无法以交互方式访问许可屏幕。 要解决此问题,必须在 Azure AD 中配置后端应用的应用注册以授予管理员同意。 这是通常由 Active Directory 管理员完成的设置更改。

  1. 打开 Azure 门户,搜索后端应用服务的研究。

  2. 找到“设置 -> 身份验证”部分。

  3. 选择标识提供者以转到身份验证应用。

  4. 在身份验证应用中,选择“管理 -> API 权限”。

  5. 选择“为默认目录授予管理员同意”。

    Azure 门户身份验证应用的屏幕截图,其中突出显示了“管理员同意”按钮。

  6. 在弹出窗口中,选择“”以确认同意。

  7. 验证“状态”列是否为“为默认目录授予”。 通过此设置,后端应用不再需要向已登录用户显示同意屏幕,并且可以直接请求访问令牌。 已登录用户有权访问 User.Read 范围设置,因为这是创建应用注册的默认范围。

    Azure 门户身份验证应用的屏幕截图,其中“状态”列显示管理员同意已授予。

2. 安装 NPM 包

在之前的教程中,后端应用不需要任何 npm 包进行身份验证,因为唯一的身份验证是通过在 Azure 门户中配置标识提供者提供的。 在本教程中,必须将已登录用户的后端 API 访问令牌交换为其范围内的 Microsoft Graph 的访问令牌。 此交换使用两个库完成,因为此交换不再使用应用服务身份验证,而是直接使用 Azure Active Directory 和 MSAL.js。

  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 令牌

若要更改 Microsoft Graph 令牌的后端 API 受众令牌,后端应用需要查找租户 ID 并将其用作 MSAL.js 配置对象的一部分。 由于后端应用将 Microsoft 配置为标识提供者,租户 ID 和其他几个必需值已在应用服务应用设置中。

示例应用中已提供以下代码。 你需要了解它存在的原因及其工作原理,以便可以将此工作成果应用于你构建的需要此功能的其他应用。

检查用于获取租户 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。 配置代理关闭请求。 然后使用 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.partner.microsoftonline.cn/BACKEND-TENANT-ID
                authority: `https://login.partner.microsoftonline.cn/${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://microsoftgraph.chinacloudapi.cn/.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,更改包括:

  • 具有对下游服务 Microsoft Graph 的 API 权限的 Active Directory 应用注册的配置,其必要范围为 User.Read
  • 授予管理员许可,让后端应用绕过用户同意屏幕。
  • 更改应用程序代码以将从前端应用发送的访问令牌转换为具有下游服务 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. 在浏览器中使用前端网站。 该 URL 的格式为 https://<front-end-app-name>.chinacloudsites.cn/。 如果令牌已过期,可能需要刷新令牌。

  2. 选择 Get user's profile。 这会将持有者令牌中的身份验证传递到后端。

  3. 后端使用你的帐户的真实 Microsoft Graph 配置文件进行响应。

    Web 浏览器的屏幕截图,其中显示了从后端应用成功获取真实配置文件后的前端应用程序。

7. 清理

在前面的步骤中,你在资源组中创建了 Azure 资源。

  1. 通过在 powershell 中运行以下命令删除资源组。 此命令可能需要花费一点时间运行。

    az group delete --name myAuthResourceGroup
    
  2. 使用身份验证应用的客户端 ID,你之前在后端和前端应用的 Enable authentication and authorization 部分中找到并记下了此 ID。

  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–表示后端应用服务无权返回 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. 使用下游客户端访问服务功能。