4

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.

Marco
  • 22,856
  • 9
  • 75
  • 124
  • I think you can use OAuth 2.0 authorization code flow to get refesh token. Then you use the refresh token to request token for you test.For more details, please refer to https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#refresh-the-access-token – Jim Xu Dec 07 '20 at 05:14
  • Thank you for the suggestion, but I think you are one step too far ahead right now. I need a valid token in the first place. A refresh token won't help right now – Marco Dec 07 '20 at 07:29
  • Hi. I suggest you use the flow to finish MFA and get refresh token. Then use the refresh token to do your test? – Jim Xu Dec 07 '20 at 07:36
  • How would I do this during an automated test on a build server? ... Maybe I should mention this in my question, if it is not clear – Marco Dec 07 '20 at 07:41
  • Since you have configured MFA in your Azure AD, we must complete MFA manually. So I think using OAuth 2.0 authorization code flow to complete MFA and get refesh token. Then use the refresh token in your test code to get access token. It is a good way. Or you use client credentials flow. – Jim Xu Dec 08 '20 at 01:44

0 Answers0