I am recently working on a project that requires front-end to call Azure REST APIs. Microsoft document describes how to get Access Token in postman via Jon Gallant's blog post. However, placing secret keys in front-end is very dangerous. In my project, I have to get the Access Token from server side in .NET. Let's see how to do it.

Preparation


In order to get an Access Token for calling Azure REST API, you must first register an application in Azure AD as described in Microsoft document. If TLDR, you can just follow these steps for a quick start.

Go to your Azure AD, App registrations, click "New registration"

Give it a name, and click "Register" to finish creating the application registration.

Go to "Certificates & secrets", click "New client secret"

In my case, I added a secret that will expires in 24 months.

Copy the Value and save it for later use. This value will display ONLY ONCE, so be sure to copy it out.

Go to the IAM of your resource that need REST API access. In my case, it's a resource group. 

Add the application registration to the resource group and give it the role you need, in my case it is "Contributor".

Get Access Token


There are many ways to get Access Token. In the official postman sample, the pre-request script will send a POST request and get the access token.

You need a client id, a tenant id, and a client secret value which we copied in previous section to get the Access Token. The other two can be copied from the application you just registered before.

pm.sendRequest({
    url: 'https://login.microsoftonline.com/' + pm.collectionVariables.get("tenantId") + '/oauth2/token',
    method: 'POST',
    header: 'Content-Type: application/x-www-form-urlencoded',
    body: {
        mode: 'urlencoded',
        urlencoded: [
            { key: "grant_type", value: "client_credentials", disabled: false },
            { key: "client_id", value: pm.collectionVariables.get("clientId"), disabled: false },
            { key: "client_secret", value: pm.collectionVariables.get("clientSecret"), disabled: false },
            { key: "resource", value: pm.collectionVariables.get("resource") || "https://management.azure.com/", disabled: false }
        ]
    }
}, function (err, res) {
    if (err) {
        console.log(err);
    } else {
        let resJson = res.json();
        pm.collectionVariables.set("bearerTokenExpiresOn", resJson.expires_on);
        pm.collectionVariables.set("bearerToken", resJson.access_token);
    }
});

In .NET, according to Microsoft, you can do it with Azure SDK for .NET libraries. But in my case, the only thing in my backend code is to return an Access Token to front-end, I don't want to use a heavy library just to use 1% of it's feature. So I decided to use the HttpClient built in .NET to do it.

The code is just a C# translation of the above postman script.

var tenantId = "<your tenant id>";
var clientId = "<your client id>";
var secret = "<your secret>";
var resourceUrl = "https://management.azure.com/";
var requestUrl = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";

// in real world application, please use Typed HttpClient from ASP.NET Core DI
var httpClient = new HttpClient();

var dict = new Dictionary<string, string>
{
    { "grant_type", "client_credentials" },
    { "client_id", clientId },
    { "client_secret", secret },
    { "resource", resourceUrl }
};

var requestBody = new FormUrlEncodedContent(dict);
var response = await httpClient.PostAsync(requestUrl, requestBody);

response.EnsureSuccessStatusCode();

var responseContent = await response.Content.ReadAsStringAsync();
var aadToken = JsonSerializer.Deserialize<AzureADToken>(responseContent);

Console.WriteLine(aadToken?.AccessToken);

The AzureADToken type

public class AzureADToken
{
    [JsonPropertyName("token_type")]
    public string TokenType { get; set; }

    [JsonPropertyName("expires_in")]
    public string ExpiresIn { get; set; }

    [JsonPropertyName("ext_expires_in")]
    public string ExtExpiresIn { get; set; 

    [JsonPropertyName("expires_on")]
    public string ExpiresOn { get; set; }

    [JsonPropertyName("not_before")]
    public string NotBefore { get; set; }

    public string Resource { get; set; }

    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }
}

Test the Access Token


Copy the token and go to postman. Try a HTTP GET request

https://management.azure.com/subscriptions/<your subscription id>/resourcegroups?api-version=2020-09-01

The token is valid and Azure REST API works fine!

Recommend Reference


To make the HttpClient works correctly in ASP.NET Core applications, please read Use IHttpClientFactory to implement resilient HTTP requests