Quellcode durchsuchen

Adds how-to guide on adding authorities to access tokens

Closes gh-542
Dmitriy Dubson vor 2 Jahren
Ursprung
Commit
e9b7089ef7

+ 1 - 0
docs/modules/ROOT/nav.adoc

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

+ 54 - 0
docs/modules/ROOT/pages/guides/how-to-custom-claims-authorities.adoc

@@ -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 - 0
docs/modules/ROOT/pages/how-to.adoc

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

+ 22 - 0
docs/src/main/java/sample/customClaims/CustomClaimsConfiguration.java

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

+ 44 - 0
docs/src/main/java/sample/customClaims/authorities/CustomClaimsWithAuthoritiesConfiguration.java

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