After setting up AzureAD with an ASP.NET Core 2.1 web application we found some of our integration tests started failing because they weren’t able to authenticate and view the app. AddAzureAD defaults to prompting the user to login with their AzureAD credentials. In the context of an integration test, we don’t usually have an interactive session.
Instead we can call AzureAD to get an access_token and use it as a bearer token header on the web app request to authenticate. In this blog post, I’ll share the steps we took to enable these integration tests.
Setup AzureAD
First we created an integration test user and added it to the enterprise application in AzureAD:
Next we found the client/tenant ids for the application registration:
We also added a new client secret for the application registration:
Not sure if this was actually necessary or not but at one point we were seeing errors about the resource not being set correctly and setting the identifierUris seemed to help.
Lastly we need the OAuth2 endpoint:
AddAzureAD and AddAzureADBearer in Startup
In the Startup.cs class we have to add and configure both AzureAD and AzureADBearer middleware.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
…services.AddAuthentication()
.AddAzureAD(options => Configuration.Bind(“AzureAd”, options))
.AddAzureADBearer(options => Configuration.Bind(“AzureAd”, options))
;
services.AddAuthorization(options =>
{
var defaultPolicyBuilder = new AuthorizationPolicyBuilder(AzureADDefaults.BearerAuthenticationScheme, AzureADDefaults.AuthenticationScheme);
defaultPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultPolicyBuilder.Build();
});…
return services.BuildServiceProvider();
}public void Configure(IApplicationBuilder app)
{
app.UseHsts();app.UseCookiePolicy();
app.UseAuthentication();
}
First we add the middleware then we configure a default authorization policy that attempts to authenticate via the bearer token then via the interactive username/password flow.
Testing
In the integration test we have to obtain an access token from AzureAD via a HTTP call and then pass the token to the secure web page as a bearer authentication header.
// Get access token from AzureAD
var tenantId = “[TODO-get tenantId from AzureAD]”;
var clientId = “[TODO-get clientId from AzureAD]”;
var form = new MultipartFormDataContent {
{new StringContent(clientId), “client_id” },
{new StringContent(“password”), “grant_type” },
{new StringContent(“itest@acme.com”), “username” },
{new StringContent(“[TODO-get password from AzureAD]”), “password” },
{new StringContent(clientId), “resource” },
{new StringContent(“openid”), “scope” },
{new StringContent(“TODO-get secret from AzureAD”), “client_secret” },
};
var accessTokenResult = await this.Client.PostAsync($”https://login.microsoftonline.com/{tenantId}/oauth2/token”, form);
var accessToken = JObject.Parse(await accessTokenResult.Content.ReadAsStringAsync());
// Add access token to http client
this.Client.DefaultRequestHeaders.Add(“Authorization”, $”{accessToken[“token_type”]} {accessToken[“access_token”]}”);
You can also test the token retrieval in Postman if you prefer:
Note – By default, the access token is good for 60 minutes. So if your authentication starts failing 60 min after you start you probably just need a new token. Obviously in a test this probably won’t be a problem but while troubleshooting the workflow I hit this a couple of times and it took me a minute to figure out why it suddenly started failing.
And that’s it. Hope someone else finds this useful. If so or you have further questions/insights, please leave a comment below.
Happy Authenticating!
Do you know what the difference is between AddAzureADBearer and AddAzureAD ? Why are both required?
AddAzureADBearer allows authenticating via an API key instead of just a user account. We specifically use this for integration testing.