Calling the Microsoft Charting API from within Azure Functions

I am trying to write a simple Azure function that calls the Microsoft Graph API. But I couldn't make the access_token work. Here's what I did:

  • Created a new Azure Function app from the Azure Portal
  • Enabled the App Service Authentication option and instructed it to log in with AAD (control mode - Express).
  • Configured an app to delegate permissions such as Login and Read User Profile to Microsoft Graph.
  • Created new JavaScript function HttpTriggerJS1
  • Changed the authorization level of this function to "Anonymous" (otherwise the default "Function" level would not allow me to run the function, always returning 401 Unauthorized)
  • The required Node ( npm install request

    ) module is installed
  • And the valid 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();
        });
    };
    
          

  • This feature has been tested in a separate browser window. Signed me up to AAD correctly.

  • But the message returned from the 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"
        }
      }
    }"
    
          

I looked at the token I got from req.headers['x-ms-token-aad-access-token']

. This is something like "AQABAA ...." which seems to be different from the usual access_token that I have seen before, starting with "eyJ ....".

What could be wrong here? When calling Graph API, should I use access_token from request headers?

Thank!

Edit:

As suggested by Chris Gillum, I also considered the flow "on behalf of". And here is my updated function that acquires an access_token for a specific resource ( https://graph.microsoft.com in my case) by providing an id_token (extracted 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();
        });
    });
};

      

+3


source to share


3 answers


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

  • Assign a default resource in your application's AAD configuration.
  • Use AAD on behalf of a stream to exchange the ID token ( x-ms-token-aad-id-token

    ) for the MS Graph access token.

The easiest approach that doesn't require any code changes is to do # 1. I describe the process in my App Auth service and Azure AD Graph API in a blog post (which needs some updates), but I'll give you Microsoft's Optimized version Graph here.

The main thing you need to do is:



  • Make sure your AAD settings contain the client secret (you already have this).
  • Make sure your AAD settings have permission to access Microsoft graphics (you've already done that).
  • Open your functional app in Resource Explorer (use the link in the portal under Platform Settings ), go to config / authsettings in the left pane, change "additionalLoginParams"

    from null

    to ["resource=https://graph.microsoft.com"]

    and save your changes.

After doing this and logging in, the request header x-ms-token-aad-access-token

will always give you an access token that works with Microsoft's timeline.

The downside to the above approach is that it won't help you if you need to access multiple AAD protected resources from your functional application. If this is a problem for you, then you will need to use approach # 2 above.

+2


source


The header must contain the appropriate access token (more details here): https://docs.microsoft.com/en-us/azure/app-service-api/app-service-api-authentication

Here's another post that falls into the same error and might be helpful: How to create an authentication token with a new microsoft api graph?



One possible workaround is to use a service master authentication flow, in which you enable your app to call the Graph API through AAD.

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

0


source


Azure Functions now support Integrated Authentication for Microsoft Graph. The docs are at https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-microsoft-graph

There is also a video at https://azure.microsoft.com/en-us/resources/videos/azure-friday-navigating-the-microsoft-graph-with-azure-functions-henderson/

For example, you can create an HttpTrigger function and add the following to the function.json function.

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

      

You can then request the Graph API on behalf of the user making the request. The access token is passed as a parameter, which you can add as a header to the 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/");
}

      

You can also trigger functions using ClientCredentials authentication mode, which means that it runs as an application and not in the context of a specific user.

0


source







All Articles