I'm logging in via Google using Duende IdentityServer, which is also configured to use ASP.NET Identity for user roles etc.
Google IdentityServer Config:
.AddGoogle(options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.ClientId = "redacted"
options.ClientSecret = "redacted";
});
Blazor UI Auth config:
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme,
options =>
{
options.Authority = "myidentityserverurl";
options.ClientId = "web2";
options.ClientSecret = "secret";
options.UsePkce = true;
options.ResponseType = "code";
options.Scope.Add("api1");
options.Scope.Add("roles");
options.ClaimActions.MapUniqueJsonKey("role", "role");
options.ClaimActions.MapUniqueJsonKey("roles", "role");
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = JwtClaimTypes.Role,
ValidateAudience = false
};
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
});
API auth config:
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer(options =>
{
options.Authority = "myidentityserverurl";
options.Audience = "api1";
options.TokenValidationParameters = new TokenValidationParameters
{
RoleClaimType = JwtClaimTypes.Role,
ValidateAudience = false
};
});
I have a Blazor UI client which can successfully login via Google, which gives me back a Cookie, an access_token and an id_token.
The access_token returned from a Google login does not have any role
claims from ASP.NET Identity, either from within Blazor or from Postman.
If I call /connect/userinfo
in IdentityServer after logging in via google, I get the role I'm in as expected:
{
"sub": "subhere",
"name": "myname",
"role": "SystemAdministrator",
"preferred_username": "uuid from google"
}
In the Blazor app, if I get the claims from httpContextAccessor.HttpContext.User.Claims
I get my role
claim as expected.
If I make a call to my API using the access token issued as part of the login to the UI, and get the claims like so:
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
I don't have any role
claim at all here. And likewise no role
claim in the bearer token in Postman after logging in via Google.
Here are the claims from the API, missing my role
claim:
[
{
"type": "iss",
"value": "myidentityserverurl"
},
{
"type": "nbf",
"value": "1653401427"
},
{
"type": "iat",
"value": "1653401427"
},
{
"type": "exp",
"value": "1653405027"
},
{
"type": "aud",
"value": "https://myidentityserverurl/resources"
},
{
"type": "scope",
"value": "openid"
},
{
"type": "scope",
"value": "profile"
},
{
"type": "scope",
"value": "api1"
},
{
"type": "scope",
"value": "roles"
},
{
"type": "http://schemas.microsoft.com/claims/authnmethodsreferences",
"value": "external"
},
{
"type": "client_id",
"value": "web2"
},
{
"type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
"value": "uuidhere"
},
{
"type": "auth_time",
"value": "1653401425"
},
{
"type": "http://schemas.microsoft.com/identity/claims/identityprovider",
"value": "Google"
},
{
"type": "sid",
"value": "A9E0A3B55D35FDAE3A7DCA0126C34E75"
},
{
"type": "jti",
"value": "43C218D3076EA595B4647CD7B369C405"
}
]
I have been trying for a few days to fix and I've run out of ideas. I think I'm missing something fundamental here.
CodePudding user response:
I think you could read the document related: http://docs.identityserver.io/en/latest/reference/profileservice.html#profile-service
and try as below:
public class ProfileServices : IProfileService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public ProfileServices(
UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
public async Task<List<Claim>> GetClaimsFromUserAsync(ApplicationUser user)
{
var claims = new List<Claim> {
new Claim(JwtClaimTypes.Subject,user.Id.ToString()),
new Claim(JwtClaimTypes.PreferredUserName,user.UserName)
};
var role = await _userManager.GetRolesAsync(user);
role.ToList().ForEach(f =>
{
claims.Add(new Claim(JwtClaimTypes.Role, f));
});
return claims;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value;
var user = await _userManager.FindByIdAsync(subjectId);
context.IssuedClaims =await GetClaimsFromUserAsync(user);
}
public async Task IsActiveAsync(IsActiveContext context)
{
var subjectId = context.Subject.Claims.FirstOrDefault(c => c.Type == "sub").Value;
var user = await _userManager.FindByIdAsync(subjectId);
context.IsActive = user != null;
}
}
Regist it in startup.cs:
services.AddScoped<IProfileService, ProfileService>();