skip to Main Content

As a follow-up on my question about how to setup a ROPC Flow. I want to access my API through the ROPC flow (currently using default user flows) and also through my web app which uses a custom policy on sign-in. This results in two different access tokens. On the left is access token received using the AcquireTokenSilent call and on the right is the access token received through postman with ROPC.
enter image description here
The custom policy token (on the left) gives an "Authorization has been denied for this request." error, while the token on the right is fine. I am assuming that the custom policy token does not work because it does not contain the tfp claim (and if it did, it would be a different one).

How can I set it up so that I can still use the ROPC flow while also using the custom policy? I would like to keep the current userjourney in the custom policy the same. Although if it is possible to somehow add ROPC as an option to it, then it would be fine.

2

Answers


  1. Chosen as BEST ANSWER

    So I finally found a way to do this in .NET Framework, if you want a solution for .NET Core you would sadly have to look somewhere else.

    In your startup add the following.

         /*
         * Configure the authorization OWIN middleware
         */
        private void ConfigureAuthenticationAzure(IAppBuilder app)
        {
            app.UseOAuthBearerAuthentication(CreateOptions(ClientId, SignUpSignInPolicy, azureDiscoveryEndpoint));
            app.UseOAuthBearerAuthentication(CreateOptions(ClientId, ApiPolicy, azureDiscoveryEndpointAPI));
        }
    
    
        private OAuthBearerAuthenticationOptions CreateOptions(string audience, string policy, string discoveryEndpoint)
        {
            var metadataEndpoint = String.Format(discoveryEndpoint, Tenant, policy);
    
            // This is the default check, in OnValidateIdentity, we check for more.
            TokenValidationParameters tvps = new TokenValidationParameters
            {
                // This is where you specify that your API only accepts tokens from its own clients
                ValidAudience = ClientId,
                ValidateAudience = true,
                AuthenticationType = policy,
                NameClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier",
                ValidateIssuer = true,
            };
    
    
            return new OAuthBearerAuthenticationOptions
            {
                AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(metadataEndpoint)),
    
                Provider = new OAuthBearerAuthenticationProvider
                {
                    OnValidateIdentity = async context =>
                    {
                        try
                        {
                            var authorizationHeader = context.Request.Headers.Get("Authorization");
                            var userJwtToken = authorizationHeader.Substring("Bearer ".Length).Trim();
    
    
                            var ticket = context.Ticket;
                            //var identity = ticket.Identity;
                            
    
                            var jwtSecurityToken = new JwtSecurityToken(userJwtToken);
                            var expiration = jwtSecurityToken.ValidTo.ToLocalTime();
                            if (expiration < DateTime.Now)
                            {
                                log.Warn("The JWT token has expired.");
                                context.Rejected();
                                return;
                            }
                            ConfigurationManager<OpenIdConnectConfiguration> configManager = new ConfigurationManager<OpenIdConnectConfiguration>(discoveryEndpoint, new OpenIdConnectConfigurationRetriever());
                            OpenIdConnectConfiguration openIdconfig = configManager.GetConfigurationAsync().Result;
                            var validationParameters = new TokenValidationParameters
                            {
                                ValidateIssuerSigningKey = true,
                                IssuerSigningKeys = openIdconfig.SigningKeys,
                                ValidateIssuer = true,
                                ValidIssuer = $"{AzureIssuer.ToLower()}/v2.0/",
                                ValidateAudience = true,
                                ValidAudience = audience,
                                ValidateLifetime = true,
                                //ClockSkew = TimeSpan.Zero
                            };
    
                            var handler = new JwtSecurityTokenHandler();
                            SecurityToken securityToken;
                            var principal = handler.ValidateToken(userJwtToken, validationParameters, out securityToken);
    
                            var policyName = principal.FindFirst("tfp")?.Value;
    
                            // Add the name claim type for this authentication type
                            if (policyName.ToLower() == DefaultPolicy.ToLower()) // Sign In Only policy...
                            {
                                // Run specific code here for the policy that just sent a token back to the application...
                                context.Validated(ticket);
                                return;
                            }
                            else if (policyName.ToLower() == SignUpSignInPolicy.ToLower())
                            {
                                context.Validated(ticket);
                                return;
                            }
                            
                            context.Rejected();
                            return;
    
                        }
                        catch(Exception ex)
                        {
                            context.Rejected();
                            return;
                        }
                    }
                }
            };
        }
    

  2. Based on the description above, you are using two policy types – a user flow and a custom policy. And, you are attempting to get SSO between the two.

    This is not a supported scenario. This is because the token uses different keys that signs the token.

    If custom policies are required for your scenario, I suggest converting the user flow ROPC to a custom policy using this document https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-ropc-policy?tabs=app-reg-ga&pivots=b2c-custom-policy

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search