Reputation: 1418
I've created an ASP.NET Core 3.1 application that uses 2 authentication types - cookie and JWT bearer.
I've setup a scheme that redirects users to the proper scheme based on the path requested:
.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = "smart";
sharedOptions.DefaultChallengeScheme = "smart";
})
.AddPolicyScheme("smart", "Bearer Authorization or Cookie", options =>
{
options.ForwardDefaultSelector = context =>
{
var requestPath = context.Request.Path;
if (CookiePolicyPathRegex.IsMatch(requestPath))
{
return CookieAuthenticationDefaults.AuthenticationScheme;
}
return JwtBearerDefaults.AuthenticationScheme;
};
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOAuthServiceScheme(Configuration); // Custom handler for JWT
I setup the authorization policies like so:
options.AddPolicy(ApiPolicies.CookiePolicy, policy =>
{
// policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
policy.RequireRole(Roles.Access);
});
options.AddPolicy(ApiPolicies.JwtPolicy, policy =>
{
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
This works fine, the proper policies are being triggered, but I have one problem. In my integration tests I use a middleware that adds the ClaimsIdentity for the cookie authentication:
public async Task Invoke(HttpContext context)
{
// Removed for brevity
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
context.User = new ClaimsPrincipal(claimsIdentity);
await _next(context);
}
The middleware is setup to run before the Auth middlewares
ConfigureAdditionalMiddleware(app);
app.UseAuthentication();
app.UseAuthorization();
If I uncomment the // policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
part from the cookie policy, the the authorization part doesn't see the Identity created in the middleware. If I leave it commented, the Identity is there, with claims, authentication type and everything. If I look in the PolicyScheme that forwards to the two auth schemes, the Identity is there.
My question is, why does adding CookieAuthenticationDefaults.AuthenticationScheme
somehow hide the User Identity that was created with the same authentication type?
Upvotes: 1
Views: 874
Reputation: 1026
The authorization middleware will evaluate your policy and will run the authentication logic which will override the user.Context
Here is the relevant code snippet (I removed and simplified the code to highlight the relevant parts):
public virtual async Task<AuthenticateResult> AuthenticateAsync(AuthorizationPolicy policy, HttpContext context)
{
if (policy.AuthenticationSchemes != null && policy.AuthenticationSchemes.Count > 0)
{
var newPrincipal = await context.AuthenticateAsync(scheme).Principal;
if (newPrincipal != null)
{
context.User = newPrincipal;
return AuthenticateResult.Success(new AuthenticationTicket(newPrincipal, string.Join(";", policy.AuthenticationSchemes)));
}
else
{
context.User = new ClaimsPrincipal(new ClaimsIdentity());
return AuthenticateResult.NoResult();
}
}
...
}
So, as you can see, when you define a scheme for your policy, then you enter the "if" statement (which will set a new context.User
, and if you comment the line, the authentication logic will not run and your custom user object will be there
Upvotes: 2