从 Azure Functions 内部调用 Microsoft Graph API

Calling Microsoft Graph API from inside Azure Functions

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

  1. 从 Azure 门户创建了一个新的 Azure Function App
  2. 打开 "App Service Authentication" 设置并指示其使用 AAD 登录(管理模式为 Express)。
  3. 已将应用配置为具有 Microsoft Graph 的委派权限,例如 "Sign in and read user profile"。
  4. 创建了一个新的 JavaScript 函数 HttpTriggerJS1
  5. 将此函数的授权级别更改为 "Anonymous"(否则默认 "Function" 级别甚至不允许我 运行 该函数,总是返回 401 Unauthorized)
  6. 安装了必要的节点模块(npm install request
  7. 而实际函数:

    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();
        });
    };
    
  8. 在单独的浏览器中测试了此功能 window。让我正确登录到 AAD。

  9. 但是从 Graph 返回的消息是:

    "{
      "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好像不一样

这里可能有什么问题?调用图形 API 时,我是否应该使用请求 headers 中的 access_token?

谢谢!

编辑:

根据 Chris Gillum 的建议,我也研究了 "on-behalf-of" 流程。这是我更新的函数,它通过提供 id_token(从请求 headers 中检索)获取特定资源的 access_token(在我的例子中是 https://graph.microsoft.com):

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();
        });
    });
};

header 应该包含正确的 access-token(此处有更多详细信息): https://docs.microsoft.com/en-us/azure/app-service-api/app-service-api-authentication

这是另一个遇到同样错误的 post,可能会有帮助: How do I create an auth token with the new microsoft graph api?

一种可能的解决方法是使用服务主体身份验证流程,您可以在其中启用函数应用程序以通过 AAD 调用 Graph API。

https://docs.microsoft.com/en-us/azure/app-service/app-service-authentication-overview#service-to-service-authentication

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

  1. 在函数应用的 AAD 配置中分配默认资源。
  2. 使用 AAD on-behalf-of flow 将您的 ID 令牌 (x-ms-token-aad-id-token) 换成 MS Graph 访问令牌。

不需要任何代码更改的最简单方法是执行#1。我在我的 App Service Auth and the Azure AD Graph API 博客 post 中概述了该过程(需要一些更新),但我将在此处为您提供 Microsoft Graph 的 Functions-optimized 版本。

您需要做的主要事情是:

  1. 确保您的 AAD 设置包括 client-secret(您已经有了)。
  2. 确保您的 AAD 设置具有访问 Microsoft Graph 的权限(您已经这样做了)。
  3. Resource Explorer 中打开您的函数应用(使用门户中 平台设置 下的 link),导航至 config/authsettings 在 left-hand 面板上,将 "additionalLoginParams"null 更改为 ["resource=https://graph.microsoft.com"],然后保存更改。

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

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

Azure Functions 现在支持 Microsoft Graph 的本机身份验证。文档位于 https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-microsoft-graph

https://azure.microsoft.com/en-us/resources/videos/azure-friday-navigating-the-microsoft-graph-with-azure-functions-henderson/

上还有一个视频

例如,您可以创建一个 HttpTrigger 函数并将以下内容添加到 function.json。

{
   "type": "token",
   "direction": "in",
   "name": "graphToken",
   "resource": "https://graph.microsoft.com",
   "identity": "userFromRequest"
}

然后,您可以代表发出请求的用户查询图表 API。访问令牌作为参数传入,您可以将其作为 header 添加到 HttpClient

using System.Net; 
using System.Net.Http; 
using System.Net.Http.Headers; 

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, string graphToken, TraceWriter log)
{
    HttpClient client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", graphToken);
    return await client.GetAsync("https://graph.microsoft.com/v1.0/me/");
}

您还可以 运行 使用 ClientCredentials 身份验证模式运行,这意味着它 运行 作为应用程序而不是在特定用户的上下文中。