Accessing Graph API with an Azure Function through Impersonation

June 21, 2020 1 By JeremyBrooks

Azure functions allow developers to focus on business logic. With the addition of the built in Authentication and Authorization feature a simple application can be developed that pulls specific information about a logged in user from graph API without having to write any code that requests access tokens on behalf of the user.

Background

The Authentication and Authorization module (formally referred to as Easy Auth) has the ability to retrieve a token on behalf of the authenticated user. Validating what information can be retrieved about the user can be accessed using the /.auth/me endpoint. The tokens can be retrieved from the function app or application code using these headers that the authentication module injects into the request. An important step is to make sure a secret is configured for the AAD App Registration that is being used by the function app.

Setting up the authentication

1. Under the Function app navigate to the Authentication/Authorization blade -> Turn the Authentication on -> Change the action from Anonymous to Azure Active Directory

2. Use Express or Advanced route. Express option creates the AAD App Registration, registers the callback URL, and creates a secret. The Advanced option requires manual setup described here.
https://docs.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad#-configure-with-advanced-settings

3. Once configured click Save

4. Navigate to https://resources.azure.com
5. Find the specific function app and navigate to the Config/authsettings section.
6. Click “Read/Write” -> Edit and add the entry below. The URL is the “audience” for the service the module will request a token for that user. Once configured select “PUT”.

"additionalLoginParams": [
  "response_type=code id_token",
  "https://graph.microsoft.com",
  "(String)"
],

7. Navigate to https://appname.azurewebsites.net/.auth/me
If you see an access_token that doesn’t start with ey…. as seen below restart the function app to make sure it picked up the change. In addition use the https://appname.azurewebsites.net/.auth/logout to clear out the current token store.

8. Once you’ve confirmed the token is visible (starting with ey….) the token can be reviewed to confirm the audience (or the endpoint the token is requested for) is the value you expected at https://jwt.ms or https://jwt.io

Function App Code

To pull the header from the application code simply reference the header X-MS-TOKEN-AAD-ACCESS-TOKEN in your application. I have an example code shown below (I go a little over the top with debug logs 🙂 ) as well as linked in Github below. To test different types of graph URLs navigate to https://developer.microsoft.com/en-us/graph/graph-explorer. This can be done in any language.

Once everything is working you should see an output like the one below with your account’s information!

 [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            var test = "test";
            log.LogInformation("C# HTTP trigger function processed a request.");
            try
            {
                string accessToken = req.Headers["X-MS-TOKEN-AAD-ACCESS-TOKEN"];
                log.LogInformation($"Token: {accessToken}");
                // Call into the Azure AD Graph API using HTTP primitives and the
                // Azure AD access token.
                var url = "https://graph.microsoft.com/v1.0/me";
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer" ,accessToken);
                log.LogInformation($"client {client.DefaultRequestHeaders.Authorization}");
                var response = await client.GetAsync(url);
                log.LogInformation($"Status Code : {response.StatusCode} ");
                log.LogInformation($"{await response.Content.ReadAsStringAsync()}");
                test = await response.Content.ReadAsStringAsync();
            }
            catch(Exception e)
            { 
                log.LogInformation(e.ToString());
            }

            return new OkObjectResult(test);
        }

References:
https://cgillum.tech/2016/03/25/app-service-auth-aad-graph-api/

Code: https://github.com/jcbrooks92/EasyAuthGraphFunction/blob/master/EasyAuthFunction/Function1.cs