从 Azure Functions 内部调用 Microsoft Graph API

我正在尝试编写一个调用 Microsoft Graph API 的简单 Azure 函数.但我无法使 access_token 工作.这是我所做的:

I'm trying to write a simple Azure Function that calls the Microsoft Graph API. But I could not make the access_token work. Here is what I've done:

  1. 从 Azure 门户创建了一个新的 Azure Function App
  2. 开启应用服务身份验证"设置,并指示其使用 AAD 登录(管理模式为 Express).
  3. 将应用配置为具有 Microsoft Graph 的登录并读取用户配置文件"等委派权限.
  4. 创建了一个新的 JavaScript 函数 HttpTriggerJS1
  5. 将此功能的授权级别更改为匿名"(否则默认情况下功能"级别甚至不允许我运行该功能,总是返回 401 Unauthorized)
  6. 安装了必要的 Node 模块(npm install request)
  7. 以及实际功能:

  1. Created a new Azure Function App from the Azure Portal
  2. Turned on the "App Service Authentication" setting and instructed it to sign in with AAD (management mode is Express).
  3. Configured the app to have delegated permissions like "Sign in and read user profile" for Microsoft Graph.
  4. Created a new JavaScript function HttpTriggerJS1
  5. Changed the authorization level of this function to "Anonymous" (otherwise by default the "Function" level would not even allow me to run the function, always returning 401 Unauthorized)
  6. Installed the necessary Node module (npm install request)
  7. And the actual function:

var request = require('request');
module.exports = function (context, req) {
    var token = req.headers['x-ms-token-aad-access-token'];
    var reqUrl = 'https://graph.microsoft.com/v1.0/me/';
    request.get(reqUrl, {'auth': {'bearer': token}}, function (err, response, msg) {
        context.res = {
            body: msg
        };
        context.done();
    });
};

  • 在单独的浏览器窗口中测试了此功能.让我正确登录到 AAD.

  • Tested this function in a separate browser window. Signed me in to AAD correctly.

    但是Graph返回的信息是:

    But the message returned from Graph was:

    "{
      "error": {
        "code": "InvalidAuthenticationToken",
        "message": "CompactToken parsing failed with error code: -2147184105",
        "innerError": {
          "request-id": "4c78551d-f0fe-4104-b1d3-e2d96fd3c02c",
          "date": "2017-05-16T19:11:14"
        }
      }
    }"
    

  • 我查看了从 req.headers['x-ms-token-aad-access-token'] 获得的令牌.它有点像AQABAA....",这似乎与我之前看到的以eyJ...."开头的常规 access_token 不同.

    I looked into the token I got from req.headers['x-ms-token-aad-access-token']. It's something like "AQABAA....", which seems different from the regular access_token I've seen before that starts with "eyJ....".

    这里可能有什么问题?调用 Graph API 时,我应该使用请求标头中的 access_token 吗?

    What could be wrong here? When calling the Graph API, am I supposed to be using the access_token from the request headers?

    谢谢!

    根据 Chris Gillum 的建议,我还研究了代表"流程.这是我更新的函数,它获取特定资源的 access_token (https://graph.microsoft.com 在我的情况下)通过提供 id_token(从请求标头中检索).:

    According to Chris Gillum's suggestion, I also looked into the "on-behalf-of" flow. And here is my updated function, which acquires an access_token for a particular resource (https://graph.microsoft.com in my case) by providing the id_token (retrieved from the request headers).:

    var request = require('request');
    
    module.exports = function (context, req) {
        var parameters = {
            grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
            client_id: process.env.WEBSITE_AUTH_CLIENT_ID,
            client_secret: process.env.WEBSITE_AUTH_CLIENT_SECRET,
            assertion: req.headers['x-ms-token-aad-id-token'],
            resource: 'https://graph.microsoft.com',
            requested_token_use: 'on_behalf_of'
        };
        request.post('https://login.microsoftonline.com/microsoft.com/oauth2/token', {form: parameters}, function (aadErr, aadResponse, aadMsg) {
            var msgJson = JSON.parse(aadMsg);
            request.get('https://graph.microsoft.com/v1.0/me/', {'auth': {'bearer': msgJson.access_token}}, function (err, response, msg) {
                context.res = {
                    body: msg
                };
                context.done();
            });
        });
    };
    

    推荐答案

    在使用 Azure 应用服务身份验证/授权时,您可以通过两种方式来完成这项工作:

    There are two ways you can make this work when using Azure App Service Authentication / Authorization:

    1. 在函数应用的 AAD 配置中分配默认资源.
    2. 使用 AAD 代表流程 将您的 ID 令牌 (x-ms-token-aad-id-token) 交换为 MS Graph 访问令牌.
    1. Assign a default resource in your function app's AAD configuration.
    2. Use the AAD on-behalf-of flow to exchange your ID token (x-ms-token-aad-id-token) for an MS Graph access token.

    不需要任何代码更改的最简单方法是执行 #1.我在 App Service Auth 和 AzureAD Graph API 博客文章(需要一些更新),但我会在此处为您提供 Microsoft Graph 的函数优化版本.

    The simplest approach which doesn't require any code changes is to do #1. I outline the process in my App Service Auth and the Azure AD Graph API blog post (which needs some updates), but I'll give you the Functions-optimized version for the Microsoft Graph here.

    你需要做的主要事情是:

    The main things you need to do are:

    1. 确保您的 AAD 设置包含客户端密码(您已经拥有该密码).
    2. 确保您的 AAD 设置有权访问 Microsoft Graph(您已经这样做了).
    3. 在资源浏览器中打开您的函数应用(使用平台设置下的门户中的链接strong>),导航到左侧面板上的 config/authsettings,将 "additionalLoginParams"null 更改为 ["resource=https://graph.microsoft.com"],然后保存更改.
    1. Ensure your AAD settings include a client-secret (you already have this).
    2. Ensure your AAD settings have the permissions to access the Microsoft Graph (you have already done this).
    3. Open your function app in Resource Explorer (use the link in the portal under Platform Settings), navigate to config/authsettings on the left-hand panel, change "additionalLoginParams" from null to ["resource=https://graph.microsoft.com"], and save the changes.

    执行此操作并再次登录后,x-ms-token-aad-access-token 请求标头将始终为您提供适用于 Microsoft Graph 的访问令牌.

    After doing this and logging in again, the x-ms-token-aad-access-token request header will always give you an access token that works with the Microsoft Graph.

    上述方法的缺点是,如果您需要从函数应用访问多个受 AAD 保护的资源,它对您没有帮助.如果这对您来说是个问题,那么您需要使用上面的方法 #2.

    The disadvantage of the above approach is that it doesn't help you if you need to access more than one AAD-protected resource from your function app. If that's a problem for you, then you'll need to use approach #2 above.

    相关文章