2
votes

I followed official steps as below to try the scenario "web app calling a Web API in Azure Ad B2C", the only difference is I am using Asp.Net core. I am using AuthorizationCode to get the access token, but it always returns with id token and NULL access token.

My code:

app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
    AuthenticationScheme = OpenIdConnectDefaults.AuthenticationScheme,
    AutomaticChallenge = true,
    ClientId = aadB2cSettings.ClientId,
    MetadataAddress = $"{aadB2cSettings.Instance}{aadB2cSettings.Tenant}/v2.0/.well-known/openid-configuration?p={aadB2cSettings.B2cSignUpOrSignInPolicy}",
    PostLogoutRedirectUri = aadB2cSettings.RedirectUrl,

    ResponseType = OpenIdConnectResponseType.CodeIdToken,
    TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name"
    },

    Events = new OpenIdConnectEvents
    {
        OnAuthorizationCodeReceived = async context =>
        {
            var authCode = context.TokenEndpointRequest.Code;
            var b2cAuthority = $"{aadB2cSettings.Instance}tfp/{aadB2cSettings.Tenant}/{aadB2cSettings.B2cSignUpOrSignInPolicy}/v2.0/.well-known/openid-configuration";
            var cca = new ConfidentialClientApplication(
                aadB2cSettings.ClientId, 
                b2cAuthority,
                aadB2cSettings.RedirectUrl, 
                new ClientCredential(aadB2cSettings.ClientSecret), 
                new TokenCache(), 
                null);

            try
            {
                var authResult = await cca.AcquireTokenByAuthorizationCodeAsync(authCode, new[] { "https://hulab2c.onmicrosoft.com/b2cdemo/all" });
                context.HandleCodeRedemption(authResult.AccessToken, authResult.IdToken);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    },

Used fiddler to capture the request, it is:

POST https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/token?p=b2c_1_signuporsignin HTTP/1.1

Request Body:

client_id=1ff91f47-08ee-4973-83f4-379ad7e0679c&client_info=1&client_secret=......&scope=https%3A%2F%2Fhulab2c.onmicrosoft.com%2Fb2cdemo%2Fall+offline_access+openid+profile&grant_type=authorization_code&code=......&redirect_uri=https%3A%2F%2Flocalhost%3A44383%2F

Return:

{"id_token":"......","token_type":"Bearer","not_before":1494494423,"client_info":"......","scope":""}

So only id token, no access token. But we should get access token here, right?

2

2 Answers

2
votes

Finally found out my failure reason: the request to get AuthorizationCode doesn't contain the target scope. Reflect in code, for OpenIdConnectOption in aspnetcore, the Scope parameter is readonly and its default value is "opened profile". Scope is readonly in OpenIdConnectOption

So the default authorization code request sent is:

GET https://login.microsoftonline.com/hulab2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signuporsignin&client_id=7f865ca0-271e-4f27-be21-6f0072fe3ad7&redirect_uri=https%3A%2F%2Flocalhost%3A44355%2Fsignin-oidc&response_type=code%20id_token&scope=openid%20profile&response_mode=form_post&nonce=...... HTTP/1.1

Thus, using this authorization code in response to get token, even we set right scope in the token request, we still can't get the access code but only id token, because the provide authorization code is only for "openid profile".

To fix this, we need to add target web api scope into the authorization code as well. Here is the how-to-fix code:

Events = new OpenIdConnectEvents
{
       OnRedirectToIdentityProvider = context =>
       {
            context.ProtocolMessage.Scope += $" offline_access {myapiscope}";

            return Task.FromResult(0);
       },
       ......
}

In AspNet, we don't need to do this because its scope is not readonly as aspnetcore and can be set directly:

new OpenIdConnectAuthenticationOptions
{
      ......
      Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}"
}