I am trying to set up integration tests in a project, where the application is secured by AzureAd (Microsoft.Identity.Web) and 2FA. Usually the user would log in to the application using a SPA, and would be greeted by the login popup or redirect. While testing controller endpoints, there is no SPA and we are not using the On-behalf-of flow, so I thought I could simply obtain a token the usual way for testing purposes:
[HttpPost]
public async Task<IActionResult> GetTokenAsync([FromBody] TokenRequest model)
{
_client.BaseAddress = new Uri("https://login.microsoftonline.com/");
var content = new FormUrlEncodedContent(new Dictionary<string, string>
{
/* ... */
}
var response = await _client.PostAsync($"{_options.TenantId}/oauth2/v2.0/token", content);
var responseContent = await response.Content.ReadAsStringAsync();
var token = JsonConvert.DeserializeObject<dynamic>(responseContent);
return Ok(new TokenResponse { Token = token.id_token });
}
However, due to 2FA being enabled, I am receiving the following error:
{{
"error": "invalid_grant",
"error_description": "AADSTS50158: External security challenge not satisfied. User will be redirected to another page or authentication provider to satisfy additional authentication
"error_codes": [
50158
],
"timestamp": "2020-12-04 12:41:10Z",
"trace_id": "xxxxxxxxxxxxxx",
"correlation_id": "xxxxxxxxxxxxxxxxxxx",
"error_uri": "https://login.microsoftonline.com/error?code=50158",
"suberror": "basic_action",
"claims": "{ /* ... */}"
}}
A more human-readable-friendly version: https://login.microsoftonline.com/error?code=50158
Here is the relevant part for the authentication configuration:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddMicrosoftIdentityWebApi(this.Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(this.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
/* ... */
}
Is there a way to satisfy the 2FA requirement during testing or circumvent it, while keeping the basic token mechanism intact?
Update
To clear up any possible confusion: I'm targeting automated tests, without human intervention. I know, that I can use a mock authentication scheme with a custom WebApplicationFactory
, but I'd go for a less fake approach if possible.