The Bajan Guy
The Bajan Guy

Reputation: 41

How to call an Azure Function App API with Easy-Auth Enables using Active Directory from a C# Client

I have an Azure Function App with Azure Active Directory configured but when I call if from my client I keep getting an Unauthorized response.

I have tried a couple different scenarios but nothing worked. Below is a snippet of the last bit of code that I tried.

                ///
                var @params2 = new NameValueCollection
                {
                    {"grant_type", "client_credentials"},
                    {"client_id", $"{ClientId}"},
                    {"client_secret", $"{ClientSecret}"},
                    {"username", userId},
                    {"resource", "https://management.azure.com/"}
                };
                var queryString2 = HttpUtility.ParseQueryString(string.Empty);
                queryString2.Add(@params2);
                var content = new FormUrlEncodedContent(new Dictionary<string, string>
                    {
                        {"grant_type", "client_credentials"},
                        {"client_id", ClientId},
                        {"client_secret", ClientSecret},
                        {"username", userId}
                    });
                var authorityUri2 = $"{string.Format(CultureInfo.InvariantCulture, AadInstance, Tenant).TrimEnd('/')}/oauth2/token";
                //var authorityUri2 = $"https://login.microsoftonline.com/{Tenant}/v2.0/.well-known/openid-configuration";
                var authUri2 = String.Format("{0}?{1}", authorityUri2, queryString2);
                var client2 = new HttpClient();
                var message = client2.PostAsync(authorityUri2, content).Result;
                //var message = client2.GetAsync(authorityUri2).Result;
                var response = message.Content.ReadAsStringAsync().Result;
                dynamic values=null;
                try
                {
                    values = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);
                }
                catch
                {
                    values = response;
                }

                var AuthToken2 = values["access_token"];
                client2.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken2);
                HttpResponseMessage response2 = await client2.GetAsync(AppBaseAddress.TrimEnd('/') + "/api/AADIntegration");

if (response.IsSuccessStatusCode)
                {
                    // Read the response and data-bind to the GridView to display To Do items.
                    string s = await response.Content.ReadAsStringAsync();
                    log.LogInformation($"Success while getting / api / AADIntegration : {s}");

                    return (ActionResult)new OkObjectResult(s);
                }
                else
                {
                    string failureDescription = await response.Content.ReadAsStringAsync();
                    log.LogInformation($"An error occurred while getting / api / AADIntegration : {response.ReasonPhrase}\n {failureDescription}");
                    return (ActionResult)new OkObjectResult(failureDescription);
                }

Data should returned from the Function App.

Upvotes: 4

Views: 3398

Answers (3)

Md Farid Uddin Kiron
Md Farid Uddin Kiron

Reputation: 22447

For client_credentials grant flow your code seems little different. Here I am giving you exact sample for azure function. Just plug and play :))

Example contains:

  1. How would you get token using client_credentials flow
  2. Getting user list From Azure Active Directory tenant using above token

Access Token Class:

public   class AccessTokenClass
    {
        public string token_type { get; set; }
        public string expires_in { get; set; }
        public string resource { get; set; }
        public string scope { get; set; }
        public string access_token { get; set; }

    }

Reference To Add:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Collections.Generic;
using System.Net.Http.Headers;

Azure Function Body:

  public static class FunctionGetUserList
    {
        [FunctionName("FunctionGetUserList")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {

            try
            {
                log.LogInformation("C# HTTP trigger function processed a request.");


                //Token Request endpoint Just replace yourTennantId/Name
                string tokenUrl = $"https://login.microsoftonline.com/yourTennantId/Name.onmicrosoft.com/oauth2/token";
                var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);

                tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
                {
                    ["grant_type"] = "client_credentials",
                    ["client_id"] = "b603c7bead87-Your_client_id-e6921e61f925",
                    ["client_secret"] = "Vxf1SluKbgu4P-Your_client_Secret-F0Nf3wE5oGl/2XDSeZ=",
                    ["resource"] = "https://graph.microsoft.com"
                });

                dynamic json;
                AccessTokenClass results = new AccessTokenClass();
                HttpClient client = new HttpClient();

                var tokenResponse = await client.SendAsync(tokenRequest);

                json = await tokenResponse.Content.ReadAsStringAsync();
                results = JsonConvert.DeserializeObject<AccessTokenClass>(json);
                var accessToken = results.access_token;

                //Create Request To Server
                using (HttpClient clientNew = new HttpClient())
                {

                    //Pass Token on header
                    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    //Get Data from API
                    var requestToAzureEndpoint = await client.GetAsync("https://graph.microsoft.com/v1.0/users");

                    if (requestToAzureEndpoint.IsSuccessStatusCode)
                    {
                        var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
                        dynamic responseResults = JsonConvert.DeserializeObject<dynamic>(result_string);

                        return new OkObjectResult(responseResults);

                    }
                    else
                    {
                        var result_string = await requestToAzureEndpoint.Content.ReadAsStringAsync();
                        return new OkObjectResult(result_string);
                    }
                }
            }
            catch (Exception ex)
            {

                return new OkObjectResult(ex.Message);
            }

        }


    }

Point To Remember

For Azure Active Directory List users access make sure you have following permission:

  1. User.Read.All
  2. Permission Type: Application

You can check here. See the screen shot for better understanding; make sure you have clicked "Grant admin consent for yourTenant" after adding permission.

enter image description here

Note: This is how you can access Azure Active Directory Token using Azure Function after that how to access resource using that token to a specific API endpoint efficiently.

Upvotes: 3

Tony Ju
Tony Ju

Reputation: 15609

The value of resource is not correct.

Replace {"resource", "https://management.azure.com/"} with {"resource", $"{ClientId}"}

Upvotes: 1

Frank H
Frank H

Reputation: 871

Are you sure you have properly implemented this properly? It looks like a few of your parameters are wrong for the client credential flow. Please double check that you are properly following the client credential flow.

The client credential grant flow is documented here : https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow

But for more information on getting this properly working in your function app, please refer to the blog below for more information/help on implementing this. https://blogs.msdn.microsoft.com/ben/2018/11/07/client-app-calling-azure-function-with-aad/

Upvotes: 1

Related Questions