How-to: Add authorities as custom claims in JWT access tokens

This guide demonstrates how to add resource owner authorities to a JWT access token. The term "authorities" may represent varying forms such as roles, permissions, or groups of the resource owner.

To make resource owner’s authorities available to the resource server, we add custom claims to the access token. When the client uses the access token to access a protected resource, the resource server will be able to obtain the information about the resource owner’s level of access, among other potential uses and benefits.

Add custom claims to JWT access tokens

You may add your own custom claims to an access token using an OAuth2TokenCustomizer<JWTEncodingContext> @Bean. Please note that this @Bean may only be defined once, and so care must be taken to ensure that you are customizing the appropriate token type — an access token in this case. If you are interested in customizing the ID Token, see the User Info Mapper guide for more information.

The following is an example of adding custom claims to an access token — in other words, every access token that is issued by the authorization server will have the custom claims populated.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;

@Configuration
public class CustomClaimsConfiguration {
	@Bean
	public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() {
		return (context) -> {
			if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
				context.getClaims().claims((claims) -> {
					claims.put("claim-1", "value-1");
					claims.put("claim-2", "value-2");
				});
			}
		};
	}
}

Add authorities as custom claims to JWT access tokens

To add authorities of the resource owner to a JWT access token, we can refer to the custom claim mapping method above and populate a custom claim with the authorities of the Principal.

We define a sample user with a set of authorities for demonstration purposes, and populate a custom claim in the access token with those authorities.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

@Configuration
public class CustomClaimsWithAuthoritiesConfiguration {
	@Bean
	public UserDetailsService users() {
		UserDetails user = User.withDefaultPasswordEncoder()
				.username("user1") (1)
				.password("password")
				.roles("user", "admin") (2)
				.build();
		return new InMemoryUserDetailsManager(user);
	}

	@Bean
	public OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() { (3)
		return (context) -> {
			if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { (4)
				context.getClaims().claims((claims) -> { (5)
					Set<String> roles = AuthorityUtils.authorityListToSet(context.getPrincipal().getAuthorities())
							.stream()
							.map(c -> c.replaceFirst("^ROLE_", ""))
							.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet)); (6)
					claims.put("roles", roles); (7)
				});
			}
		};
	}
}
1 Define a sample user user1 with an in-memory UserDetailsService.
2 Assign the roles for user1.
3 Define an OAuth2TokenCustomizer<JwtEncodingContext> @Bean that allows for customizing the JWT claims.
4 Check whether the JWT is an access token.
5 Access the default claims via the JwtEncodingContext.
6 Extract the roles from the Principal object. The role information is stored as a string prefixed with ROLE_, so we strip the prefix here.
7 Set the custom claim roles to the set of roles collected from the previous step.

As a result of this customization, authorities information about the user will be included as a custom claim in the access token.