Adds how-to guide on adding authorities to access tokens

Closes gh-542
This commit is contained in:
Dmitriy Dubson
2023-06-12 18:47:14 -04:00
committed by Joe Grandja
parent d749ce91c1
commit e9b7089ef7
5 changed files with 122 additions and 0 deletions

View File

@@ -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[]

View File

@@ -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 owners 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.

View File

@@ -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]

View File

@@ -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");
});
}
};
}
}

View File

@@ -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>
});
}
};
}
}