问题
I have a service which is responsible for authenticating users.
After updating:
- IdentityServer4 from 2.3.2 to 4.0.2;
an issue popped up:
- The token does not contain the required user claims anymore.
The service is configured this way:
- The Startup.cs contains:
applicationBuilder.UseCookiePolicy();
applicationBuilder.UseIdentityServer();
applicationBuilder.UseAuthorization();
//...
mvcCoreBuilder.AddAuthorization(ConfigureAuthorization);
var auth = mvcCoreBuilder.Services.AddAuthentication(ConfigureAuthenticationOptions);
auth.AddIdentityServerAuthentication(ConfigureIdentityServerAuthentication);
//...
void ConfigureAuthenticationOptions(AuthenticationOptions authenticationOptions)
{
authenticationOptions.DefaultScheme = IdentityServerConstants.DefaultCookieAuthenticationScheme;
}
//...
void ConfigureAuthorization(AuthorizationOptions authorizationOptions)
{
var requirements = new List<IAuthorizationRequirement> { new DenyAnonymousAuthorizationRequirement() };
var schemes = new List<string> { IdentityServerConstants.DefaultCookieAuthenticationScheme };
authorizationOptions.DefaultPolicy = new AuthorizationPolicy(requirements, schemes);
}
- And the (Updated for IS4 4.0.2) Login controller which sets the cookie contains:
props = new _AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration),
};
// user.Claims contains the "role" claim
var claimsIdentity = new ClaimsIdentity(user.Claims, CookieAuthenticationDefaults.AuthenticationScheme);
claimsIdentity.AddClaim(new Claim(JwtClaimTypes.Subject, user.SubjectId));
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
// The created claimsPrincipal contains the "role" claim
await HttpContext.SignInAsync(claimsPrincipal, props);
- Before updating the IS4 the controller looked like this:
await HttpContext.SignInAsync(user.SubjectId, props, user.Claims.ToArray());
In the api project which is being protected:
- When trying to validate for controller requests i have (in a different api project):
// attribute for validating roles on controllers
[AuthorizeRoles(Role.SimpleRole)] // which implements IAuthorizationFilter
// And the implementation:
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
// user is of type 'ClaimsIdentity'
// and it does not contain the "role" claim
// some checks to verify the user here...
}
The startup of the protected api project contains:
builder.AddAuthorization(ConfigureAuthorization);
var auth = builder.Services.AddAuthentication(ConfigureAuthenticationOptions);
auth.AddIdentityServerAuthentication(ConfigureIdentityServerAuthentication);
The ConfigureIdentityServerAuthentication
method sets some IdentityServerAuthenticationOptions
. The RoleClaimType
is not being set because it have a default value 'role'
which is the expected one.
The IdentityServerAuthenticationOptions
is from the 'IdentityServer4.AccessTokenValidation' package version 3.0.1.
Here are two screenshots to prove that the RoleClaimType
is set:
on the authorization service on Startup.cs:
on the OnAuthorization method from the
AuthorizeRoles
attribute:
The Questions:
- Why the token does not contain all of the claims which are added to the ClaimsIdentity object?
- How can this be fixed in order for the token to contain the "role" claim again?
Involved Technologies:
- Net Core 3.1
- IdentityServer4 4.0.2
回答1:
by default IdentityServer and ASP.NET core have different opinion on what the name of the RoleClaim should be.
Add this code in your client to fix that mapping (setting the TokenValidationParameters option)
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/User/Login";
options.LogoutPath = "/User/Logout";
options.AccessDeniedPath = "/User/AccessDenied";
}).AddOpenIdConnect(options =>
{
options.Authority = "https://localhost:6001";
options.ClientId = "authcodeflowclient";
options.ClientSecret = "mysecret";
options.ResponseType = "code";
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("email");
options.Scope.Add("employee_info");
//Map the custom claims that should be included
options.ClaimActions.MapUniqueJsonKey("employment_start", "employment_start");
options.ClaimActions.MapUniqueJsonKey("seniority", "seniority");
options.ClaimActions.MapUniqueJsonKey("contractor", "contractor");
options.ClaimActions.MapUniqueJsonKey("employee", "employee");
options.ClaimActions.MapUniqueJsonKey("management", "management");
options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Role, JwtClaimTypes.Role);
options.SaveTokens = true;
options.SignedOutRedirectUri = "/";
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.Name,
RoleClaimType = JwtClaimTypes.Role,
};
options.Prompt = "consent";
});
Also, it can be helpful to look at the various requests in Fiddler to figure out your claim issues.
Settings this one to True can also help out:
options.GetClaimsFromUserInfoEndpoint = true;
来源:https://stackoverflow.com/questions/63016021/role-claim-is-missing-from-the-token-net-core-3-1-is4