When I apply
[Authorize(Policies.ManagesProducts, AuthenticationSchemes = "Bearer,Cookies")]
Unauthenticated user gets redirected to login page.
However, when I change the order of schemes
[Authorize(Policies.ManagesProducts, AuthenticationSchemes = "Cookies,Bearer")]
Browser just gets:
> HTTP401: DENIED - The requested resource requires user authentication.
So in what order are challenges applied?
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
Hi @voroninp I opened an issue similar to your, maybe you can find the discussion there useful to better understand your point. Take a look here
@EnricoMassoneDeltatre Thanks, very helpful.
@EnricoMassoneDeltatre Thanks, very helpful.
You are welcome.
@EnricoMassoneDeltatre , do I get it write that ForwardDefaultSelector does not provide the authentication phase, so I have to detect it myself? I would like to dynamically forward only challenge.
Hi @voroninp could you please explain what you would like to achieve ?
I'm not sure that I fully understood your question. Do you want a mean to only forward the authentication challenge but not, saying, the authentication itself ?
@EnricoMassoneDeltatre exactly, try as it is, and if not authenticated redirect to another scheme for challenge.
I think it is possible.
If you a look at this documentation you can find the following example:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options => options.ForwardChallenge = "Google")
.AddGoogle(options => { });
}
In this scenario you are saying that the default scheme for authentication is cookie.
Now let's suppose you decorate the action method HomeController.Index with the Authorize attribute. This is the flow you will get when you call the path /home/index:
Yes, but then it is unconditional, not dynamic as with ForwardDefaultSelector.
I have a UI and API parts of my service. And for UI I allow both bearer and cookies, but want to challenge with cookies auth, if user is not authenticated. For API part I want only bearer auth.
In other words I would like to have ForwardChallengeSelector =)
I'm not a hundred percent sure about it, but I think that it's not possible. Basically the problem is that you don't have a ForwardChallengeSelector on the options object.
I think that in your case a good approach should be the following:
ForwardDefaultSelector on the cookie authentication which should decide whether or not forwarding everything to the bearer scheme. You need a criteria to decide whether or not the incoming request is an API request. Usually the routes for api calls starts with/api, you can use that condition in order to decide if the call is for an API endpoint. In the casi of an API call all the authentication logic is delegated to the bearer scheme. Otherwise you can assume that the call is for a UI endpoint and you can avoid to forward (this is achieved by returning null from the function)Doing so you will lose the ability to call the UI endpoint by only using a bearer token, basically you are asking your UI users to always send a cookie along with the request (but if you use a web browser you get this behavior for free). Is this really an issue for you ?
@EnricoMassoneDeltatre No, not an issue at all because to make browser always send a bearer token user needs to have an installed extension which sets the Authorize header appropriately. Otherwise cookies is the only option.
@EnricoMassoneDeltatre No, not an issue at all because to make browser always send a bearer token user needs to have an installed extension which sets the
Authorizeheader appropriately. Otherwisecookiesis the only option.
I agree with you, I would implement the solution explained above.
Maybe you can ask for a clarification to the asp.net core guys if you want to be completely sure that this is the only viable solution.
@EnricoMassoneDeltatre Btw, maybe you know the answer to this issue as well?
I need to allow my test environment API's accessed by using different policies. Normal users to use a B2C based SignIn policy, and the automated tests under DevOps pipeline uses ROPC based sign in policy. So, both the tokens are issued by B2C but with different policies.
But even if the token is issued by ROPC policy, it is going to default SignIn policy based validations. Any idea how to make the token validation flow goes to respective authentication scheme based on policy?
Code is as like below.
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer("ROPC", jwtOptions =>
{
jwtOptions.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
// Accept only those tokens where the audience of the token is equal to the client ID of this application
ValidAudience = Configuration["AppSettings:AAD:ClientId"],
AuthenticationType = Configuration["AppSettings:AAD:ROPCSignInPolicyId"],
};
jwtOptions.Authority = $"https://{Configuration["AppSettings:AAD:Tenant"]}/{Configuration["AppSettings:AAD:B2CDirectoryName"]}.onmicrosoft.com/Configuration["AppSettings:AAD:ROPCSignInPolicyId"]/v2.0/";
jwtOptions.Audience = Configuration["AppSettings:AAD:ClientId"];
})
.AddJwtBearer(jwtOptions =>
{
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{Configuration["AppSettings:AAD:Tenant"]}/{Configuration["AppSettings:AAD:SignInPolicyId"]}/";
jwtOptions.Audience = Configuration["AppSettings:AAD:ClientId"];
jwtOptions.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidIssuer = $"https://login.microsoftonline.com/tfp/{Configuration["AppSettings:AAD:Tenant"]}/v2.0/",
ValidateAudience = true,
ValidateLifetime = false,
ValidateTokenReplay = true,
ValidateIssuer = false,
ValidateIssuerSigningKey = true,
};
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = DefaultAuthenticationFailed,
OnTokenValidated = DefaultOnTokenValidated
};
});
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder("ROPC",
JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
Moved to Master issue for: Authorize with a specific scheme #16190
Challenges are applied in order, but not all schemes play nicely with each other, multiple bearers can be challenged without issue, but cookies results in a 302, and bearer results in a 401, so only one of those schemes will win (last one)
Thanks for contacting us. We believe that the question you've raised have been answered. If you still feel a need to continue the discussion, feel free to reopen it and add your comments.