Roles not added in .NET claims - ID Token

When following these steps https://docs.kinde.com/developer-tools/guides/dotnet-open-id-connect/ the claims not have any roles with them. What do i do wrong?
7 Replies
prullebakje.
prullebakje.OP6d ago
Found out that i needed the "ID Token" instead of the "Acccess Token".. But there is no "Roles (array)" option available. Why is that?
Ages - Kinde
Ages - Kinde6d ago
Hi, thanks for your message! Just a quick note — roles are included in the access token, not the ID token. If you’re not seeing them, you’ll likely just need to enable that option: 1. Go to Settings > Applications > [Your application] > Tokens 2. Scroll to the Token Customization section 3. Click Customize on the Access tokens card 4. Under Additional claims, toggle on Roles (array) You can find the full guide here:
🔗 https://docs.kinde.com/build/tokens/token-customization/ Once that’s enabled, roles should start appearing in your access token as expected. Let me know how it goes — happy to help check the token content if needed!
prullebakje.
prullebakje.OP6d ago
Ok, but when following https://docs.kinde.com/developer-tools/guides/dotnet-open-id-connect/ this is not supported?
TotalScrub
TotalScrub6d ago
I'm not sure what your referring to as not being supported, but this is how I parse the roles Kinde sends back in the access token. It looks like it sends back a JSON payload (sorry it's been a while since I looked at this) which I need to parse and then extract only the role names. This is what I use:
private readonly Lazy<string[]> _roles = new(() =>
{
return principal.Claims.Where(c => string.Equals(c.Type, "roles", StringComparison.OrdinalIgnoreCase))
.Select(c => JsonConvert.DeserializeObject<Role>(c.Value)?.Key!)
.ToArray();
});

private record Role(string Id, string Key, string Name, string Description);
private readonly Lazy<string[]> _roles = new(() =>
{
return principal.Claims.Where(c => string.Equals(c.Type, "roles", StringComparison.OrdinalIgnoreCase))
.Select(c => JsonConvert.DeserializeObject<Role>(c.Value)?.Key!)
.ToArray();
});

private record Role(string Id, string Key, string Name, string Description);
You'll also need to turn on the roles in the access token like Abdelrahman has outlined
Abdelrahman Zaki - Kinde
Thanks so much for jumping in and sharing that, @TotalScrub — really appreciate you helping out here! @prullebakje., just checking in — are you still running into any issues after enabling roles in the access token and adjusting your parsing logic as Stephen outlined?
prullebakje.
prullebakje.OP5d ago
There are three different types of roles: - Access Token - ID Token - M2M Token The roles option, is only available as an option for the Access Token, so not the ID Token. I'm using the ID Token in my application because i'm using OIDC. So information is missing that i really need. I found a solution to it. By implementing the following. I read the access token claims and transfer them to the ID token claims after validation. Then the roles will be available in the ID token claims.
builder.Services.AddOpenIdConnect(options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = context =>
{
// Get the "Access Token" not the "ID Token"
var accessToken = context.TokenEndpointResponse?.AccessToken;

// Read the token contents
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(accessToken);

// Read the roles from the claims and map them to new claims format
var roles = jwtToken.Claims.Where(claim => claim.Type == "roles")
.Select(claim => JsonSerializer.Deserialize<RoleModel>(claim.Value, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}))
.ToList();

var roleClaims = roles.Select(role => new Claim("roles", role.Key)).ToList();


// Add claims to the principal
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
claimsIdentity?.AddClaims(roleClaims);

return Task.CompletedTask;
}
}
}
);
builder.Services.AddOpenIdConnect(options =>
{
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = context =>
{
// Get the "Access Token" not the "ID Token"
var accessToken = context.TokenEndpointResponse?.AccessToken;

// Read the token contents
var handler = new JwtSecurityTokenHandler();
var jwtToken = handler.ReadJwtToken(accessToken);

// Read the roles from the claims and map them to new claims format
var roles = jwtToken.Claims.Where(claim => claim.Type == "roles")
.Select(claim => JsonSerializer.Deserialize<RoleModel>(claim.Value, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}))
.ToList();

var roleClaims = roles.Select(role => new Claim("roles", role.Key)).ToList();


// Add claims to the principal
var claimsIdentity = context.Principal.Identity as ClaimsIdentity;
claimsIdentity?.AddClaims(roleClaims);

return Task.CompletedTask;
}
}
}
);
Only thing that needs to happen now: how can i refresh the claims on the fly? When the roles change for a user.
Ages - Kinde
Ages - Kinde5d ago
Thanks for sharing the solution — great job implementing the role mapping from the access token into the ID token claims! To keep claims in sync when roles change, here are a few ways you can refresh them dynamically: --- 1. Use Refresh Tokens to Keep Claims Updated - Ensure your app is configured to use refresh tokens. - You can shorten the access token lifetime to force more frequent refreshes. - When user roles are updated (via the UI or API), Kinde automatically invalidates the cached claims. --- 2. Force a Claims Refresh via the Management API - Use the following endpoint to invalidate the user’s cached claims:
POST /api/v1/users/{user_id}/refresh_claims - This ensures the next token refresh will include the updated roles and other claims. --- 3. Periodic Claim Sync (Polling) - Store role information locally on login. - Periodically poll the Management API to check for updates and update the identity context if needed. --- 4. Webhook-Based Updates - Subscribe to Kinde’s webhooks to be notified when user attributes (like roles) change. - Your system can then trigger a claims refresh or session update automatically. --- More on this here: Sync with Kinde Let me know if this helps or if you’d like any guidance setting it up!
Kinde docs
Keep your product in sync with Kinde
Our developer tools provide everything you need to get started with Kinde.

Did you find this page helpful?