Getting an Azure OAuth2 token with a Service Principal
Tags: azure, dotnet, testing
Published: 2022-11-16 17:25:20


Trying to call the Azure REST APIs requires an OAuth2 token. This seems simple right? Ha ha unfortunately it's not.

There are a number of authentication methods to get a token using the [AzureDefaultCredential] (https://learn.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential?view=azure-dotnet) class. Out of these "fall through" authentication options for local development I couldn't get the [VisualStudioCredential] (https://learn.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocredential?view=azure-dotnet) option to work for me. This is the option to use your user credentials from Visual Studio. Seems like my company has some sort of difference between my local AD login and my Azure login. I was able to use the [Azure CLI login] (https://learn.microsoft.com/en-us/dotnet/api/azure.identity.azureclicredential?view=azure-dotnet) for local development by logging in via command line but that doesn't help me when the tests are running in Azure Devops. Tests running in Azure Devops can also use the [ManagedIdentityCredential] (https://learn.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential?view=azure-dotnet) to get a token but I hadn't gotten to that stage yet and I still needed to get an Azure OAuth2 token for local development. 

So I chose to try using an Azure Service Principal because it can be used when developing locally. This requires setting up an App Registration in Azure AD. This [Azure article] (https://learn.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal#register-an-application-with-azure-ad-and-create-a-service-principal) is very good on how to setup the App Registration. Note that setting up the App Registration and assigning the Service Principal to your Application Role requires sufficient permissions. In my case I was a Subscription Owner.

Note that while setting up your App Registration you need to save the AzureSubscriptionId, ClientId, and ClientSecret(value) to use when requesting the OAuth2 token. Here's a method to get the token:

```
public async Task<string> GetAzureOAuthToken()
        {
            var requestUrl = AuthEndpoint + "/" + AzureSubscriptionId + "/oauth2/token";
            var client = CreateHttpClient();
            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("client_id", ClientId));
            values.Add(new KeyValuePair<string, string>("client_secret", ClientSecret));
            values.Add(new KeyValuePair<string, string>("grant_type", GrantType));
            values.Add(new KeyValuePair<string, string>("resource", Resource));

            var requestBody = new FormUrlEncodedContent(values);
            var response = await client.PostAsync(requestUrl, requestBody);
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            string responseStr = await response.Content.ReadAsStringAsync();
            var content = JsonConvert.DeserializeObject<AzureADTokenResponse>(responseStr);

            return content.access_token;
        }
```
Other parameters needed for the above code:

AuthEndpoint = "https://login.microsoftonline.com"
grant_type = "client_credentials"
resource = "https://management.core.windows.net"

The object *AzureADTokenResponse* in the code above is a C# class I created to deserialize the response from the request. Below is the code for this class.

```
public class AzureADTokenResponse
    {
        public string? token_type { get; set; }

        public string? expires_in { get; set; }

        public string? ext_expires_in { get; set; }

        public string? expires_on { get; set; }

        public string? not_before { get; set; }

        public string? resource { get; set; }

        public string? access_token { get; set; }
    }
```
Now if you deserialize the response without errors than grabbing the token is easy:

*return content.access_token;*

Ok you've got your Azure OAuth2 token. Now you have to try and use it to call Azure REST APIs. I'll have another post on how to call Azure Logic App APIs soon :)