Careeroza
Customizing Tokens
Adding Roles to the Access Token
By default the access token has sub, aud, scope, iss, exp. Resource servers usually also want the user's roles. Add them with an OAuth2TokenCustomizer:
@Bean
OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer(CustomerRepository customers) {
return context -> {
if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
Authentication principal = context.getPrincipal();
Set<String> roles = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.filter(a -> a.startsWith("ROLE_"))
.map(a -> a.substring(5))
.collect(Collectors.toSet());
context.getClaims().claim("roles", roles);
context.getClaims().audience(List.of("shopease-api"));
}
if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {
customers.findByUsername(context.getPrincipal().getName()).ifPresent(c -> context.getClaims()
.claim("name", c.getFullName())
.claim("email", c.getEmail()));
}
};
}The Result
Decoded token
Decode the payload part of the new access token:
echo "$ACCESS_TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jqExample output
{
"sub": "asha",
"aud": ["shopease-api"],
"nbf": 1772340000,
"scope": ["openid", "orders.read"],
"roles": ["CUSTOMER"],
"iss": "http://localhost:9000",
"exp": 1772340600,
"iat": 1772340000,
"jti": "b6a3e0d2-5f41-4c8e-9a7b-1d2e3f4a5b6c"
}On the resource server, map this flat claim without custom code: JwtGrantedAuthoritiesConverter with setAuthoritiesClaimName("roles") and setAuthorityPrefix("ROLE_").
Note: Tokens are signed, not encrypted. Anyone holding a token can read its payload. Never put secrets, passwords, Aadhaar or card numbers in claims.
Practice
Exercise: Add a tier claim ("GOLD" / "SILVER") to access tokens only for the shopease-web client.
Show answer
@Bean
OAuth2TokenCustomizer<JwtEncodingContext> tierCustomizer(CustomerRepository customers) {
return context -> {
boolean accessToken = OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType());
boolean webClient = "shopease-web".equals(context.getRegisteredClient().getClientId());
if (accessToken && webClient) {
customers.findByUsername(context.getPrincipal().getName())
.ifPresent(c -> context.getClaims().claim("tier", c.getTier().name()));
}
};
}