Skip to content

Adds how-to guide on adding authorities to access tokens #1264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
** xref:guides/how-to-pkce.adoc[]
** xref:guides/how-to-social-login.adoc[]
** xref:guides/how-to-userinfo.adoc[]
** xref:guides/how-to-custom-claims-authorities.adoc[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
[[how-to-custom-claims-authorities]]
= How-to: Add authorities as custom claims in JWT-based access tokens
:index-link: ../how-to.html
:docs-dir: ..

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 owners' authorities available to the resource server, we add custom claims to an access token issued by Spring Authorization Server.
The client using the issued token to access protected resources will then have information about the resource owner’s level of access, among other potential uses and benefits.

* xref:guides/how-to-custom-claims-authorities.adoc#custom-claims[Add custom claims to JWT access tokens]
* xref:guides/how-to-custom-claims-authorities.adoc#custom-claims-authorities[Add authorities as custom claims to JWT access tokens]

[[custom-claims]]
== Add custom claims to JWT access tokens

You may add your own custom claims to an access token using `OAuth2TokenCustomizer<JWTEncodingContext>` bean.
Please note that this bean may only be defined once, and so care must be taken care of to ensure that you are customizing the appropriate token type — an access token in this case.
If you are interested in customizing the identity token, see xref:guides/how-to-userinfo.adoc#customize-user-info-mapper[the UserInfo 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.

[[sample.customClaims]]
[source,java]
----
include::{examples-dir}/main/java/sample/customClaims/CustomClaimsConfiguration.java[]
----

[[custom-claims-authorities]]
== Add authorities as custom claims to JWT access tokens

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

We define a sample user with a mix of authorities for demonstration purposes, and populate custom claims in an access token
with those authorities.

[[sample.customClaims.authorities]]
[source,java]
----
include::{examples-dir}/main/java/sample/customClaims/authorities/CustomClaimsWithAuthoritiesConfiguration.java[]
----

<1> Define a sample user `user1` with an in-memory user details service.
<2> Define a few roles for `user1`.
<3> Define `OAuth2TokenCustomizer<JwtEncodingContext>` `@Bean` that allows for customizing JWT token claims.
<4> Check whether the JWT token is an access token.
<5> From the encoding context, modify the claims of the access token.
<6> Extract user roles from the `Principal` object. The role information for internal users is stored as a string prefixed with `ROLE_`, so we strip the prefix here.
<7> Set 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 within the
access token.
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/how-to.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
* xref:guides/how-to-ext-grant-type.adoc[Implement an Extension Authorization Grant Type]
* xref:guides/how-to-userinfo.adoc[Customize the OpenID Connect 1.0 UserInfo response]
* xref:guides/how-to-jpa.adoc[Implement core services with JPA]
* xref:guides/how-to-custom-claims-authorities.adoc[Add authorities as custom claims in JWT-based access tokens]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package sample.customClaims;

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");
});
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package sample.customClaims.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(new String[] { "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>
});
}
};
}
}