Three Ways to get an OAuth2 Access Token for API Testing for Azure AD Secured APIs

Image

March 1, 2023

Automated testing has never been more critical in improving the frequency of releases without sacrificing quality.   API tests are often used to validate functional requirements and run much faster than UI tests.   One challenge with executing API tests is that many modern websites and the APIs are protected by Azure Active Directory (AAD) identity.  Fortunately there are some different approaches for obtaining the credentials to securely call the APIs from the automated API tests.  In this post I will discuss some of the various ways to accomplish this.

First, before we talk about how we get the credentials to call the service let's discuss how to call a secured API.  Most modern applications use OAuth2 to allow authorized users access to the APIs.  One of the benefits of OAuth2 is flexible and allows different types of applications to be authorized in different ways. The different ways are called Grants.  I'll mention the names but I'll focus more on the how to use it.  Once you are have been authorized by one of these approaches, you will have an access token to call the API. To make an authorized call to the service, a valid access token is passed as an authorization header in each call.  So regardless of which of the following approaches below is used, each will call the service the same way, if you would like to learn more about API and over types of software testing, you may want to visit a company similar to Parasoft to find out more.

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);HttpResponseMessage response = httpClient.GetAsync(todoListBaseAddress   "/api/todolist").Result;

The following examples are using ADAL.NET library Microsoft.IdentityModel.Clients.ActiveDirectory that can be downloaded from NuGet.

Use a Native App to sign on via Resource Owner Password Credential Grant

I mentioned that an app registered as a web app/API app doesn't have a flow to handle accepting the username and password to retrieve the access token.  However, the other option when registering an app allows you to register an app as a Native app.  This approach is called Resource Owner Password Credential Flow and requires a username and password for authentication.  A native application is typically a mobile app and the app includes the sign on page.  If you or an AAD admin can register a native app, this can be used for automated testing.  I like this approach the best because the sign on can be done by code and it uses an account to retrieve specific authorization roles/claims.  This allows you tests to validate specific authorization rules in you API for different test users and roles.  There are some downsides to this approach and doesn't work in all scenarios.  This approach does not work with Microsoft Accounts (MSA), Multi-Factor Authentication (MFA), and if you application requires consent.

To register a native application in Azure, navigate from the portal to AAD > App Registrations > New App Registration.

Grant permission for the new app registration to have access to API.  Open the app registration and choose Settings > Required Permissions > API Access > Select Service

Add the permission for API Access

Click on Grant Permission to delegate permission to all users. If you fail to do this you will see an error message similiar to this below.

AADSTS65001: The user or administrator has not consented to use the application.

The clientId is the application id from the new Native App registration.

string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
UserPasswordCredential uc = new UserPasswordCredential(user, password);
var authContext = new AuthenticationContext(authority);
var result = authContext.AcquireTokenAsync(todoListResourceId, clientId, uc).Result;
accessToken = result.AccessToken;

More info

http://www.cloudidentity.com/blog/2014/07/08/using-adal-net-to-authenticate-users-via-usernamepassword/

Send the Client Secret via Client Credentials Grant

If creating a native app isn't an option, a similar alternative is to use the client application secret key from the AAD app registration settings.  This option is is known as Client Credentials Grant.  This requires a the ability to securely keep the secret key of the application.  Ensure that this is the key for the QA / test environment and it is a separate key than what is used in the application in case it is compromised.  One downside to this approach is that the client secret is global for the application so you would have to use something else to test roles / claims authorization in the API.

To create the key, navigate to you app registration > Settings > Keys to create a new key and Save.

add-client-secret.jpg

Set this clientSecret equal to the key.

string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
var authContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientId, clientSecret);
var result = authContext.AcquireTokenAsync(todoListResourceId, clientCredential).Result;
accessToken = result.AccessToken;

More info

https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/Client-credential-flows

Use a automated UI Test to get the access token via Authorization Code Grant

The third option is not as straight forward as calling an API but this option doesn't require the application administrators giving any kind of secret or building something separately.  This option requires the assistance of a UI test to use the application's sign on page.  This allows the application's normal authentication process to occur.  Once your UI test signs in to the application, the access token is stored in local storage.  I am using Selenium in this case and within Selenium, the JavaScript executor allows access to the DOM and application settings such as local storage.  Use the browser dev tools to look to see the token name.  Once, we have the access token, we can use it in all of our tests as long as stay valid.  Probably the biggest downside of this approach is that a different tool has to be used to get the token.  It can still be done headless so it doesn't require an agent to be configured to run UI tests.

// UI Sign on Test
var driver = Browser.Open(url, browser);
SignOnPage signonPage = new SsoSignOn(driver);
var homePage = signOnPage.Logon(username, password);
// Retrieve Access Token after user is signed in
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
string accessToken = (string)js.ExecuteScript("return window.localStorage.getItem('adal.access.token.keycd123')");

Only Retrieving the Access Token once and Keeping the Access Token Valid

The typical access token stays valid for 1 hour. If you are using the 3rd approach and retrieving the access token using a UI test, you probably don't want this to have to occur for every API test.  Additionally, if you API tests run longer than an hour, you will need to handle with your code.   You could use the singleton pattern to only retrieve the token once and keep it in memory if it has already been retrieved.  The access token also states how long it is going to be valid.  In addition to retrieving the stored token, check to see if the token is close to expiring.  If the token is 15 minutes from expiring, retrieve a new access token with a new 1 hour expiration to continue running tests.  This sounds like a good next post.

As always, if you have any questions or comments, reach out to me on Twitter at @mikedouglasdev.