diff --git a/pom.xml b/pom.xml
index b66e4053..a535849f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,200 +1,204 @@
- 4.0.0
- com.baeldung
- spring-security-oauth
- 1.0.0-SNAPSHOT
-
- spring-security-oauth
- pom
-
-
- org.springframework.boot
- spring-boot-starter-parent
- 2.1.17.RELEASE
-
-
-
-
- spring-security-oauth
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven-compiler-plugin.version}
-
- 1.8
- 1.8
-
-
-
-
- org.apache.maven.plugins
- maven-war-plugin
- ${maven-war-plugin.version}
-
- false
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven-surefire-plugin.version}
-
- true
-
- **/*IntegrationTest.java
- **/*LiveTest.java
- **/*MvcTest.java
-
-
-
-
-
-
-
-
-
- 2.3.5.RELEASE
- 1.0.10.RELEASE
- 2.9.2
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
+ com.baeldung
+ spring-security-oauth
+ 1.0.0-SNAPSHOT
+
+ spring-security-oauth
+ pom
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.17.RELEASE
+
+
+
+
+ spring-security-oauth
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ ${maven-war-plugin.version}
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ true
+
+ **/*IntegrationTest.java
+ **/*LiveTest.java
+ **/*MvcTest.java
+
+
+
+
+
+
+
+
+
+ 2.3.5.RELEASE
+ 1.0.10.RELEASE
+ 2.9.2
2.1.1.RELEASE
2.1.3.RELEASE
-
- 2.6
+
+ 2.6
-
- 3.8.0
- 3.2.2
-
+
+ 3.8.0
+ 3.2.2
+
-
- oauth-legacy/oauth-resource-server-legacy-2
- oauth-legacy/oauth-resource-server-legacy-1
- oauth-rest/oauth-resource-server
- oauth-jwt/jwt-resource-server
- oauth-sso/sso-resource-server
- oauth-resource-server/resource-server-jwt
- oauth-resource-server/resource-server-opaque
- oauth-authorization-server/resource-server
+
+ oauth-legacy/oauth-resource-server-legacy-2
+ oauth-legacy/oauth-resource-server-legacy-1
+ oauth-rest/oauth-resource-server
+ oauth-jwt/jwt-resource-server
+ oauth-sso/sso-resource-server
+ oauth-resource-server/resource-server-jwt
+ oauth-resource-server/resource-server-opaque
+ oauth-authorization-server/resource-server
- oauth-legacy/oauth-ui-implicit-angularjs-legacy
- oauth-legacy/oauth-ui-password-angularjs-legacy
+ oauth-legacy/oauth-ui-implicit-angularjs-legacy
+ oauth-legacy/oauth-ui-password-angularjs-legacy
clients-SPA-legacy/clients-js-only-react-legacy
clients-SPA-legacy/oauth-resource-server-auth0-legacy
- oauth-rest/oauth-authorization-server
- oauth-legacy/oauth-authorization-server-legacy
- oauth-jwt/jwt-auth-server
- oauth-sso/sso-authorization-server
- oauth-legacy/oauth-zuul-gateway
+ oauth-rest/oauth-authorization-server
+ oauth-legacy/oauth-authorization-server-legacy
+ oauth-jwt/jwt-auth-server
+ oauth-sso/sso-authorization-server
+ oauth-legacy/oauth-zuul-gateway
oauth-legacy/oauth-microservices
- oauth-resource-server/authorization-server
- oauth-authorization-server/spring-authorization-server
-
- oauth-jws-jwk-legacy\oauth-authorization-server-jws-jwk-legacy
- oauth-jws-jwk-legacy\oauth-resource-server-jws-jwk-legacy
-
- oauth-sso/sso-client-app-1
- oauth-sso/sso-client-app-2
- oauth-authorization-server/client-server
-
-
-
-
-
-
-
- integration
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- integration-test
-
- test
-
-
-
- **/*LiveTest.java
-
-
- **/*IntegrationTest.java
-
-
-
-
-
-
- json
-
-
-
-
-
-
-
-
- live
-
-
-
- org.codehaus.cargo
- cargo-maven2-plugin
-
-
- start-server
- pre-integration-test
-
- start
-
-
-
- stop-server
- post-integration-test
-
- stop
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- integration-test
-
- test
-
-
-
- none
-
-
- **/*LiveTest.java
-
-
- cargo
-
-
-
-
-
-
-
-
-
-
+ oauth-resource-server/authorization-server
+ oauth-authorization-server/spring-authorization-server
+
+ oauth-jws-jwk-legacy\oauth-authorization-server-jws-jwk-legacy
+ oauth-jws-jwk-legacy\oauth-resource-server-jws-jwk-legacy
+
+ oauth-sso/sso-client-app-1
+ oauth-sso/sso-client-app-2
+ oauth-authorization-server/client-server
+
+ redis-authorization-server/redis-auth-client
+ redis-authorization-server/redis-oauth-server
+ redis-authorization-server/redis-resource-server
+
+
+
+
+
+
+
+ integration
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ **/*LiveTest.java
+
+
+ **/*IntegrationTest.java
+
+
+
+
+
+
+ json
+
+
+
+
+
+
+
+
+ live
+
+
+
+ org.codehaus.cargo
+ cargo-maven2-plugin
+
+
+ start-server
+ pre-integration-test
+
+ start
+
+
+
+ stop-server
+ post-integration-test
+
+ stop
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ integration-test
+
+ test
+
+
+
+ none
+
+
+ **/*LiveTest.java
+
+
+ cargo
+
+
+
+
+
+
+
+
+
+
diff --git a/redis-authorization-server/.gitignore b/redis-authorization-server/.gitignore
new file mode 100644
index 00000000..fbdf7ebb
--- /dev/null
+++ b/redis-authorization-server/.gitignore
@@ -0,0 +1,3 @@
+/.classpath
+/.project
+/.settings/
diff --git a/redis-authorization-server/pom.xml b/redis-authorization-server/pom.xml
new file mode 100644
index 00000000..0eb53b96
--- /dev/null
+++ b/redis-authorization-server/pom.xml
@@ -0,0 +1,23 @@
+
+ 4.0.0
+ redis-authorization-server
+
+ com.baeldung
+ redis-authorization-server
+ 0.1.0-SNAPSHOT
+ pom
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.3
+
+
+
+
+ redis-oauth-server
+ redis-auth-client
+ redis-resource-server
+
+
diff --git a/redis-authorization-server/redis-auth-client/.gitignore b/redis-authorization-server/redis-auth-client/.gitignore
new file mode 100644
index 00000000..d1451cc0
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/.gitignore
@@ -0,0 +1,5 @@
+/target/
+/.classpath
+/.factorypath
+/.project
+/.settings/
diff --git a/redis-authorization-server/redis-auth-client/pom.xml b/redis-authorization-server/redis-auth-client/pom.xml
new file mode 100644
index 00000000..19058ef3
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/pom.xml
@@ -0,0 +1,62 @@
+
+ 4.0.0
+
+ com.baeldung
+ redis-auth-client
+ redis-auth-client
+ 0.1.0-SNAPSHOT
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.3
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
+
+ org.springframework
+ spring-webflux
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
+ eugen@baeldung.com
+ Eugen Paraschiv
+ https://github.com/eugenp
+ eugenp
+
+
+
+
+ UTF-8
+ 17
+
+
+
diff --git a/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/RedisAuthClientApplication.java b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/RedisAuthClientApplication.java
new file mode 100644
index 00000000..8a7f6fd2
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/RedisAuthClientApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.oauth.client;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class RedisAuthClientApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(RedisAuthClientApplication.class, args);
+ }
+}
diff --git a/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/SecurityConfig.java b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/SecurityConfig.java
new file mode 100644
index 00000000..3177ab3b
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/SecurityConfig.java
@@ -0,0 +1,21 @@
+package com.baeldung.oauth.client.config;
+
+import static org.springframework.security.config.Customizer.withDefaults;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+@EnableWebSecurity
+public class SecurityConfig {
+
+ @Bean
+ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest()
+ .authenticated())
+ .oauth2Login(oauth2Login -> oauth2Login.loginPage("/oauth2/authorization/articles-client-oidc"))
+ .oauth2Client(withDefaults());
+ return http.build();
+ }
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/WebClientConfig.java b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/WebClientConfig.java
new file mode 100644
index 00000000..b018bff8
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/config/WebClientConfig.java
@@ -0,0 +1,39 @@
+package com.baeldung.oauth.client.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
+import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
+import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
+import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@Configuration
+public class WebClientConfig {
+
+ @Bean
+ WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
+ ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
+ return WebClient.builder()
+ .apply(oauth2Client.oauth2Configuration())
+ .build();
+ }
+
+ @Bean
+ OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository,
+ OAuth2AuthorizedClientRepository authorizedClientRepository) {
+
+ OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
+ .authorizationCode()
+ .refreshToken()
+ .build();
+ DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository,
+ authorizedClientRepository);
+ authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
+
+ return authorizedClientManager;
+ }
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/web/ArticlesController.java b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/web/ArticlesController.java
new file mode 100644
index 00000000..0c7e6189
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/src/main/java/com/baeldung/oauth/client/web/ArticlesController.java
@@ -0,0 +1,29 @@
+package com.baeldung.oauth.client.web;
+
+import static org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
+
+import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
+import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.reactive.function.client.WebClient;
+
+@RestController
+public class ArticlesController {
+
+ public ArticlesController(WebClient webClient) {
+ this.webClient = webClient;
+ }
+
+ private WebClient webClient;
+
+ @GetMapping(value = "/articles")
+ public String[] getArticles(@RegisteredOAuth2AuthorizedClient("articles-client-authorization-code") OAuth2AuthorizedClient authorizedClient) {
+ return this.webClient.get()
+ .uri("http://127.0.0.1:8090/articles")
+ .attributes(oauth2AuthorizedClient(authorizedClient))
+ .retrieve()
+ .bodyToMono(String[].class)
+ .block();
+ }
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-auth-client/src/main/resources/application.yml b/redis-authorization-server/redis-auth-client/src/main/resources/application.yml
new file mode 100644
index 00000000..9f9d6a3f
--- /dev/null
+++ b/redis-authorization-server/redis-auth-client/src/main/resources/application.yml
@@ -0,0 +1,35 @@
+server:
+ port: 8085
+
+logging:
+ level:
+ root: INFO
+ org.springframework.web: INFO
+ org.springframework.security: INFO
+ org.springframework.security.oauth2: INFO
+ org.springframework.boot: INFO
+
+spring:
+ security:
+ oauth2:
+ client:
+ registration:
+ articles-client-oidc:
+ provider: spring
+ client-id: articles-client
+ client-secret: secret
+ authorization-grant-type: authorization_code
+ redirect-uri: "http://127.0.0.1:8085/login/oauth2/code/{registrationId}"
+ scope: openid
+ client-name: articles-client-oidc
+ articles-client-authorization-code:
+ provider: spring
+ client-id: articles-client
+ client-secret: secret
+ authorization-grant-type: authorization_code
+ redirect-uri: "http://127.0.0.1:8085/authorized"
+ scope: articles.read
+ client-name: articles-client-authorization-code
+ provider:
+ spring:
+ issuer-uri: http://localhost:9000
\ No newline at end of file
diff --git a/redis-authorization-server/redis-oauth-server/.gitignore b/redis-authorization-server/redis-oauth-server/.gitignore
new file mode 100644
index 00000000..d1451cc0
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/.gitignore
@@ -0,0 +1,5 @@
+/target/
+/.classpath
+/.factorypath
+/.project
+/.settings/
diff --git a/redis-authorization-server/redis-oauth-server/pom.xml b/redis-authorization-server/redis-oauth-server/pom.xml
new file mode 100644
index 00000000..069909c2
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/pom.xml
@@ -0,0 +1,67 @@
+
+ 4.0.0
+
+ com.baeldung
+ redis-oauth-server
+ redis-oauth-server
+ 0.1.0-SNAPSHOT
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.3
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-authorization-server
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ org.testcontainers
+ testcontainers
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
+ eugen@baeldung.com
+ Eugen Paraschiv
+ https://github.com/eugenp
+ eugenp
+
+
+
+
+ UTF-8
+ 17
+ 3.4.3
+
+
+
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/RedisOAuth2AuhorizationServerApplication.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/RedisOAuth2AuhorizationServerApplication.java
new file mode 100644
index 00000000..eae5de4f
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/RedisOAuth2AuhorizationServerApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.redis;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class RedisOAuth2AuhorizationServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(RedisOAuth2AuhorizationServerApplication.class, args);
+ }
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RedisConfig.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RedisConfig.java
new file mode 100644
index 00000000..8811b663
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RedisConfig.java
@@ -0,0 +1,96 @@
+package com.baeldung.redis.config;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.convert.RedisCustomConversions;
+import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.utility.DockerImageName;
+
+import com.baeldung.redis.convert.BytesToClaimsHolderConverter;
+import com.baeldung.redis.convert.BytesToOAuth2AuthorizationRequestConverter;
+import com.baeldung.redis.convert.BytesToUsernamePasswordAuthenticationTokenConverter;
+import com.baeldung.redis.convert.ClaimsHolderToBytesConverter;
+import com.baeldung.redis.convert.OAuth2AuthorizationRequestToBytesConverter;
+import com.baeldung.redis.convert.UsernamePasswordAuthenticationTokenToBytesConverter;
+import com.baeldung.redis.repository.OAuth2AuthorizationGrantAuthorizationRepository;
+import com.baeldung.redis.repository.OAuth2RegisteredClientRepository;
+import com.baeldung.redis.repository.OAuth2UserConsentRepository;
+import com.baeldung.redis.service.RedisOAuth2AuthorizationConsentService;
+import com.baeldung.redis.service.RedisOAuth2AuthorizationService;
+import com.baeldung.redis.service.RedisRegisteredClientRepository;
+
+import jakarta.annotation.PreDestroy;
+
+@EnableRedisRepositories("com.baeldung.redis.repository")
+@Configuration(proxyBeanMethods = false)
+public class RedisConfig {
+
+ private static GenericContainer> redis;
+
+ static {
+ redis = new GenericContainer<>(DockerImageName.parse("redis:5.0.3-alpine")).withExposedPorts(6379);
+ redis.start();
+ System.setProperty("spring.redis.host", redis.getHost());
+ System.setProperty("spring.redis.port", redis.getMappedPort(6379)
+ .toString());
+ }
+
+ @PreDestroy
+ public void preDestroy() throws IOException {
+ redis.stop();
+ }
+
+ @Bean
+ @Order(1)
+ public RedisConnectionFactory redisConnectionFactory() {
+ RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redis.getHost(), redis.getMappedPort(6379));
+ return new LettuceConnectionFactory(config);
+ }
+
+ @Bean
+ @Order(2)
+ public RedisTemplate, ?> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+ RedisTemplate redisTemplate = new RedisTemplate<>();
+ redisTemplate.setConnectionFactory(redisConnectionFactory);
+ return redisTemplate;
+ }
+
+ @Bean
+ @Order(3)
+ public RedisCustomConversions redisCustomConversions() {
+ return new RedisCustomConversions(Arrays.asList(new UsernamePasswordAuthenticationTokenToBytesConverter(),
+ new BytesToUsernamePasswordAuthenticationTokenConverter(), new OAuth2AuthorizationRequestToBytesConverter(),
+ new BytesToOAuth2AuthorizationRequestConverter(), new ClaimsHolderToBytesConverter(), new BytesToClaimsHolderConverter()));
+ }
+
+ @Bean
+ @Order(4)
+ public RedisRegisteredClientRepository registeredClientRepository(OAuth2RegisteredClientRepository registeredClientRepository) {
+ RedisRegisteredClientRepository redisRegisteredClientRepository = new RedisRegisteredClientRepository(registeredClientRepository);
+ redisRegisteredClientRepository.save(RegisteredClients.messagingClient());
+ return redisRegisteredClientRepository;
+ }
+
+ @Bean
+ @Order(5)
+ public RedisOAuth2AuthorizationService authorizationService(RegisteredClientRepository registeredClientRepository,
+ OAuth2AuthorizationGrantAuthorizationRepository authorizationGrantAuthorizationRepository) {
+ return new RedisOAuth2AuthorizationService(registeredClientRepository, authorizationGrantAuthorizationRepository);
+ }
+
+ @Bean
+ @Order(6)
+ public RedisOAuth2AuthorizationConsentService authorizationConsentService(OAuth2UserConsentRepository userConsentRepository) {
+ return new RedisOAuth2AuthorizationConsentService(userConsentRepository);
+ }
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RegisteredClients.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RegisteredClients.java
new file mode 100644
index 00000000..5aa49202
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/RegisteredClients.java
@@ -0,0 +1,34 @@
+package com.baeldung.redis.config;
+
+import java.util.UUID;
+
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.core.oidc.OidcScopes;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
+
+public class RegisteredClients {
+
+ public static RegisteredClient messagingClient() {
+ return RegisteredClient.withId(UUID.randomUUID()
+ .toString())
+ .clientId("articles-client")
+ .clientSecret("{noop}secret")
+ .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
+ .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+ .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
+ .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+ .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
+ .redirectUri("http://127.0.0.1:8085/authorized")
+ .redirectUri("http://127.0.0.1:8085/login/oauth2/code/articles-client-oidc")
+ .postLogoutRedirectUri("http://127.0.0.1:9000/login")
+ .scope(OidcScopes.OPENID)
+ .scope("articles.read")
+ .clientSettings(ClientSettings.builder()
+ .requireAuthorizationConsent(true)
+ .build())
+ .build();
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/SecurityConfig.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/SecurityConfig.java
new file mode 100644
index 00000000..f09fbf32
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/config/SecurityConfig.java
@@ -0,0 +1,63 @@
+/**
+ *
+ */
+package com.baeldung.redis.config;
+
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
+import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+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.crypto.factory.PasswordEncoderFactories;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
+import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
+import org.springframework.security.provisioning.InMemoryUserDetailsManager;
+import org.springframework.security.web.SecurityFilterChain;
+
+@EnableWebSecurity
+@Configuration
+@EnableAutoConfiguration(exclude = { JpaRepositoriesAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
+@ComponentScan
+public class SecurityConfig {
+
+ @Bean
+ @Order(1)
+ SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
+ OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
+ http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
+ .oidc(Customizer.withDefaults());
+ return http.formLogin(Customizer.withDefaults())
+ .build();
+ }
+
+ @Bean
+ @Order(2)
+ SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
+ http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.anyRequest()
+ .authenticated())
+ .formLogin(Customizer.withDefaults());
+ return http.build();
+ }
+
+ @Bean
+ UserDetailsService users() {
+ PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+ UserDetails user = User.builder()
+ .username("admin")
+ .password("password")
+ .passwordEncoder(encoder::encode)
+ .roles("USER")
+ .build();
+ return new InMemoryUserDetailsManager(user);
+ }
+
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToClaimsHolderConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToClaimsHolderConverter.java
new file mode 100644
index 00000000..6f7fedeb
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToClaimsHolderConverter.java
@@ -0,0 +1,29 @@
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.ReadingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
+
+import com.baeldung.redis.entity.OAuth2AuthorizationGrantAuthorization;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@ReadingConverter
+public class BytesToClaimsHolderConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public BytesToClaimsHolderConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToClaimsHolderConverter.class.getClassLoader()));
+ objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
+ objectMapper.addMixIn(OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class, ClaimsHolderMixin.class);
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class);
+ }
+
+ @Override
+ public OAuth2AuthorizationGrantAuthorization.ClaimsHolder convert(byte[] value) {
+ return this.serializer.deserialize(value);
+ }
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToOAuth2AuthorizationRequestConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToOAuth2AuthorizationRequestConverter.java
new file mode 100644
index 00000000..d40b1622
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToOAuth2AuthorizationRequestConverter.java
@@ -0,0 +1,29 @@
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.ReadingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@ReadingConverter
+public class BytesToOAuth2AuthorizationRequestConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public BytesToOAuth2AuthorizationRequestConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToOAuth2AuthorizationRequestConverter.class.getClassLoader()));
+ objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationRequest.class);
+ }
+
+ @Override
+ public OAuth2AuthorizationRequest convert(byte[] value) {
+ return this.serializer.deserialize(value);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToUsernamePasswordAuthenticationTokenConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToUsernamePasswordAuthenticationTokenConverter.java
new file mode 100644
index 00000000..70c1fdf9
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/BytesToUsernamePasswordAuthenticationTokenConverter.java
@@ -0,0 +1,28 @@
+
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.ReadingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@ReadingConverter
+public class BytesToUsernamePasswordAuthenticationTokenConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public BytesToUsernamePasswordAuthenticationTokenConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToUsernamePasswordAuthenticationTokenConverter.class.getClassLoader()));
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, UsernamePasswordAuthenticationToken.class);
+ }
+
+ @Override
+ public UsernamePasswordAuthenticationToken convert(byte[] value) {
+ return this.serializer.deserialize(value);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderMixin.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderMixin.java
new file mode 100644
index 00000000..4720765f
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderMixin.java
@@ -0,0 +1,20 @@
+package com.baeldung.redis.convert;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+abstract class ClaimsHolderMixin {
+
+ @JsonCreator
+ ClaimsHolderMixin(@JsonProperty("claims") Map claims) {
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderToBytesConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderToBytesConverter.java
new file mode 100644
index 00000000..046218dd
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/ClaimsHolderToBytesConverter.java
@@ -0,0 +1,31 @@
+
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.WritingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
+
+import com.baeldung.redis.entity.OAuth2AuthorizationGrantAuthorization;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@WritingConverter
+public class ClaimsHolderToBytesConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public ClaimsHolderToBytesConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(ClaimsHolderToBytesConverter.class.getClassLoader()));
+ objectMapper.registerModules(new OAuth2AuthorizationServerJackson2Module());
+ objectMapper.addMixIn(OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class, ClaimsHolderMixin.class);
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationGrantAuthorization.ClaimsHolder.class);
+ }
+
+ @Override
+ public byte[] convert(OAuth2AuthorizationGrantAuthorization.ClaimsHolder value) {
+ return this.serializer.serialize(value);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/OAuth2AuthorizationRequestToBytesConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/OAuth2AuthorizationRequestToBytesConverter.java
new file mode 100644
index 00000000..70e42027
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/OAuth2AuthorizationRequestToBytesConverter.java
@@ -0,0 +1,30 @@
+
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.WritingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@WritingConverter
+public class OAuth2AuthorizationRequestToBytesConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public OAuth2AuthorizationRequestToBytesConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(OAuth2AuthorizationRequestToBytesConverter.class.getClassLoader()));
+ objectMapper.registerModules(new OAuth2AuthorizationServerJackson2Module());
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, OAuth2AuthorizationRequest.class);
+ }
+
+ @Override
+ public byte[] convert(OAuth2AuthorizationRequest value) {
+ return this.serializer.serialize(value);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/UsernamePasswordAuthenticationTokenToBytesConverter.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/UsernamePasswordAuthenticationTokenToBytesConverter.java
new file mode 100644
index 00000000..1e660528
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/convert/UsernamePasswordAuthenticationTokenToBytesConverter.java
@@ -0,0 +1,27 @@
+package com.baeldung.redis.convert;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.data.convert.WritingConverter;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@WritingConverter
+public class UsernamePasswordAuthenticationTokenToBytesConverter implements Converter {
+
+ private final Jackson2JsonRedisSerializer serializer;
+
+ public UsernamePasswordAuthenticationTokenToBytesConverter() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModules(SecurityJackson2Modules.getModules(BytesToUsernamePasswordAuthenticationTokenConverter.class.getClassLoader()));
+ this.serializer = new Jackson2JsonRedisSerializer<>(objectMapper, UsernamePasswordAuthenticationToken.class);
+ }
+
+ @Override
+ public byte[] convert(UsernamePasswordAuthenticationToken value) {
+ return this.serializer.serialize(value);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationCodeGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationCodeGrantAuthorization.java
new file mode 100644
index 00000000..3275776c
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationCodeGrantAuthorization.java
@@ -0,0 +1,55 @@
+package com.baeldung.redis.entity;
+
+import java.security.Principal;
+import java.time.Instant;
+import java.util.Set;
+
+import org.springframework.data.redis.core.index.Indexed;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+
+public class OAuth2AuthorizationCodeGrantAuthorization extends OAuth2AuthorizationGrantAuthorization {
+
+ private final Principal principal;
+
+ private final OAuth2AuthorizationRequest authorizationRequest;
+
+ private final AuthorizationCode authorizationCode;
+
+ @Indexed
+ private final String state;
+
+ public OAuth2AuthorizationCodeGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes,
+ AccessToken accessToken, RefreshToken refreshToken, Principal principal, OAuth2AuthorizationRequest authorizationRequest,
+ AuthorizationCode authorizationCode, String state) {
+ super(id, registeredClientId, principalName, authorizedScopes, accessToken, refreshToken);
+ this.principal = principal;
+ this.authorizationRequest = authorizationRequest;
+ this.authorizationCode = authorizationCode;
+ this.state = state;
+ }
+
+ public Principal getPrincipal() {
+ return this.principal;
+ }
+
+ public OAuth2AuthorizationRequest getAuthorizationRequest() {
+ return this.authorizationRequest;
+ }
+
+ public AuthorizationCode getAuthorizationCode() {
+ return this.authorizationCode;
+ }
+
+ public String getState() {
+ return this.state;
+ }
+
+ public static class AuthorizationCode extends AbstractToken {
+
+ public AuthorizationCode(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ }
+
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationGrantAuthorization.java
new file mode 100644
index 00000000..6aa19979
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2AuthorizationGrantAuthorization.java
@@ -0,0 +1,158 @@
+package com.baeldung.redis.entity;
+
+import java.time.Instant;
+import java.util.Map;
+import java.util.Set;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.redis.core.RedisHash;
+import org.springframework.data.redis.core.index.Indexed;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
+
+@RedisHash("oauth2_authorization")
+public abstract class OAuth2AuthorizationGrantAuthorization {
+
+ @Id
+ private final String id;
+
+ private final String registeredClientId;
+
+ private final String principalName;
+
+ private final Set authorizedScopes;
+
+ private final AccessToken accessToken;
+
+ private final RefreshToken refreshToken;
+
+ protected OAuth2AuthorizationGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes,
+ AccessToken accessToken, RefreshToken refreshToken) {
+ this.id = id;
+ this.registeredClientId = registeredClientId;
+ this.principalName = principalName;
+ this.authorizedScopes = authorizedScopes;
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getRegisteredClientId() {
+ return this.registeredClientId;
+ }
+
+ public String getPrincipalName() {
+ return this.principalName;
+ }
+
+ public Set getAuthorizedScopes() {
+ return this.authorizedScopes;
+ }
+
+ public AccessToken getAccessToken() {
+ return this.accessToken;
+ }
+
+ public RefreshToken getRefreshToken() {
+ return this.refreshToken;
+ }
+
+ protected abstract static class AbstractToken {
+
+ @Indexed
+ private final String tokenValue;
+
+ private final Instant issuedAt;
+
+ private final Instant expiresAt;
+
+ private final boolean invalidated;
+
+ protected AbstractToken(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated) {
+ this.tokenValue = tokenValue;
+ this.issuedAt = issuedAt;
+ this.expiresAt = expiresAt;
+ this.invalidated = invalidated;
+ }
+
+ public String getTokenValue() {
+ return this.tokenValue;
+ }
+
+ public Instant getIssuedAt() {
+ return this.issuedAt;
+ }
+
+ public Instant getExpiresAt() {
+ return this.expiresAt;
+ }
+
+ public boolean isInvalidated() {
+ return this.invalidated;
+ }
+
+ }
+
+ public static class ClaimsHolder {
+
+ private final Map claims;
+
+ public ClaimsHolder(Map claims) {
+ this.claims = claims;
+ }
+
+ public Map getClaims() {
+ return this.claims;
+ }
+
+ }
+
+ public static class AccessToken extends AbstractToken {
+
+ private final OAuth2AccessToken.TokenType tokenType;
+
+ private final Set scopes;
+
+ private final OAuth2TokenFormat tokenFormat;
+
+ private final ClaimsHolder claims;
+
+ public AccessToken(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated, OAuth2AccessToken.TokenType tokenType,
+ Set scopes, OAuth2TokenFormat tokenFormat, ClaimsHolder claims) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ this.tokenType = tokenType;
+ this.scopes = scopes;
+ this.tokenFormat = tokenFormat;
+ this.claims = claims;
+ }
+
+ public OAuth2AccessToken.TokenType getTokenType() {
+ return this.tokenType;
+ }
+
+ public Set getScopes() {
+ return this.scopes;
+ }
+
+ public OAuth2TokenFormat getTokenFormat() {
+ return this.tokenFormat;
+ }
+
+ public ClaimsHolder getClaims() {
+ return this.claims;
+ }
+
+ }
+
+ public static class RefreshToken extends AbstractToken {
+
+ public RefreshToken(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ }
+
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2ClientCredentialsGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2ClientCredentialsGrantAuthorization.java
new file mode 100644
index 00000000..65d5c408
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2ClientCredentialsGrantAuthorization.java
@@ -0,0 +1,12 @@
+package com.baeldung.redis.entity;
+
+import java.util.Set;
+
+public class OAuth2ClientCredentialsGrantAuthorization extends OAuth2AuthorizationGrantAuthorization {
+
+ public OAuth2ClientCredentialsGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes,
+ AccessToken accessToken) {
+ super(id, registeredClientId, principalName, authorizedScopes, accessToken, null);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2DeviceCodeGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2DeviceCodeGrantAuthorization.java
new file mode 100644
index 00000000..fa013aa7
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2DeviceCodeGrantAuthorization.java
@@ -0,0 +1,68 @@
+
+package com.baeldung.redis.entity;
+
+import java.security.Principal;
+import java.time.Instant;
+import java.util.Set;
+
+import org.springframework.data.redis.core.index.Indexed;
+
+public class OAuth2DeviceCodeGrantAuthorization extends OAuth2AuthorizationGrantAuthorization {
+
+ private final Principal principal;
+
+ private final DeviceCode deviceCode;
+
+ private final UserCode userCode;
+
+ private final Set requestedScopes;
+
+ @Indexed
+ private final String deviceState;
+
+ public OAuth2DeviceCodeGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes, AccessToken accessToken,
+ RefreshToken refreshToken, Principal principal, DeviceCode deviceCode, UserCode userCode, Set requestedScopes, String deviceState) {
+ super(id, registeredClientId, principalName, authorizedScopes, accessToken, refreshToken);
+ this.principal = principal;
+ this.deviceCode = deviceCode;
+ this.userCode = userCode;
+ this.requestedScopes = requestedScopes;
+ this.deviceState = deviceState;
+ }
+
+ public Principal getPrincipal() {
+ return this.principal;
+ }
+
+ public DeviceCode getDeviceCode() {
+ return this.deviceCode;
+ }
+
+ public UserCode getUserCode() {
+ return this.userCode;
+ }
+
+ public Set getRequestedScopes() {
+ return this.requestedScopes;
+ }
+
+ public String getDeviceState() {
+ return this.deviceState;
+ }
+
+ public static class DeviceCode extends AbstractToken {
+
+ public DeviceCode(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ }
+
+ }
+
+ public static class UserCode extends AbstractToken {
+
+ public UserCode(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ }
+
+ }
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2RegisteredClient.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2RegisteredClient.java
new file mode 100644
index 00000000..b532585f
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2RegisteredClient.java
@@ -0,0 +1,226 @@
+
+package com.baeldung.redis.entity;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Set;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.redis.core.RedisHash;
+import org.springframework.data.redis.core.index.Indexed;
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
+import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
+import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
+import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
+
+@RedisHash("oauth2_registered_client")
+public class OAuth2RegisteredClient {
+
+ @Id
+ private final String id;
+
+ @Indexed
+ private final String clientId;
+
+ private final Instant clientIdIssuedAt;
+
+ private final String clientSecret;
+
+ private final Instant clientSecretExpiresAt;
+
+ private final String clientName;
+
+ private final Set clientAuthenticationMethods;
+
+ private final Set authorizationGrantTypes;
+
+ private final Set redirectUris;
+
+ private final Set postLogoutRedirectUris;
+
+ private final Set scopes;
+
+ private final ClientSettings clientSettings;
+
+ private final TokenSettings tokenSettings;
+
+ public OAuth2RegisteredClient(String id, String clientId, Instant clientIdIssuedAt, String clientSecret, Instant clientSecretExpiresAt, String clientName,
+ Set clientAuthenticationMethods, Set authorizationGrantTypes, Set redirectUris,
+ Set postLogoutRedirectUris, Set scopes, ClientSettings clientSettings, TokenSettings tokenSettings) {
+ this.id = id;
+ this.clientId = clientId;
+ this.clientIdIssuedAt = clientIdIssuedAt;
+ this.clientSecret = clientSecret;
+ this.clientSecretExpiresAt = clientSecretExpiresAt;
+ this.clientName = clientName;
+ this.clientAuthenticationMethods = clientAuthenticationMethods;
+ this.authorizationGrantTypes = authorizationGrantTypes;
+ this.redirectUris = redirectUris;
+ this.postLogoutRedirectUris = postLogoutRedirectUris;
+ this.scopes = scopes;
+ this.clientSettings = clientSettings;
+ this.tokenSettings = tokenSettings;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getClientId() {
+ return this.clientId;
+ }
+
+ public Instant getClientIdIssuedAt() {
+ return this.clientIdIssuedAt;
+ }
+
+ public String getClientSecret() {
+ return this.clientSecret;
+ }
+
+ public Instant getClientSecretExpiresAt() {
+ return this.clientSecretExpiresAt;
+ }
+
+ public String getClientName() {
+ return this.clientName;
+ }
+
+ public Set getClientAuthenticationMethods() {
+ return this.clientAuthenticationMethods;
+ }
+
+ public Set getAuthorizationGrantTypes() {
+ return this.authorizationGrantTypes;
+ }
+
+ public Set getRedirectUris() {
+ return this.redirectUris;
+ }
+
+ public Set getPostLogoutRedirectUris() {
+ return this.postLogoutRedirectUris;
+ }
+
+ public Set getScopes() {
+ return this.scopes;
+ }
+
+ public ClientSettings getClientSettings() {
+ return this.clientSettings;
+ }
+
+ public TokenSettings getTokenSettings() {
+ return this.tokenSettings;
+ }
+
+ public static class ClientSettings {
+
+ private final boolean requireProofKey;
+
+ private final boolean requireAuthorizationConsent;
+
+ private final String jwkSetUrl;
+
+ private final JwsAlgorithm tokenEndpointAuthenticationSigningAlgorithm;
+
+ private final String x509CertificateSubjectDN;
+
+ public ClientSettings(boolean requireProofKey, boolean requireAuthorizationConsent, String jwkSetUrl,
+ JwsAlgorithm tokenEndpointAuthenticationSigningAlgorithm, String x509CertificateSubjectDN) {
+ this.requireProofKey = requireProofKey;
+ this.requireAuthorizationConsent = requireAuthorizationConsent;
+ this.jwkSetUrl = jwkSetUrl;
+ this.tokenEndpointAuthenticationSigningAlgorithm = tokenEndpointAuthenticationSigningAlgorithm;
+ this.x509CertificateSubjectDN = x509CertificateSubjectDN;
+ }
+
+ public boolean isRequireProofKey() {
+ return this.requireProofKey;
+ }
+
+ public boolean isRequireAuthorizationConsent() {
+ return this.requireAuthorizationConsent;
+ }
+
+ public String getJwkSetUrl() {
+ return this.jwkSetUrl;
+ }
+
+ public JwsAlgorithm getTokenEndpointAuthenticationSigningAlgorithm() {
+ return this.tokenEndpointAuthenticationSigningAlgorithm;
+ }
+
+ public String getX509CertificateSubjectDN() {
+ return this.x509CertificateSubjectDN;
+ }
+
+ }
+
+ public static class TokenSettings {
+
+ private final Duration authorizationCodeTimeToLive;
+
+ private final Duration accessTokenTimeToLive;
+
+ private final OAuth2TokenFormat accessTokenFormat;
+
+ private final Duration deviceCodeTimeToLive;
+
+ private final boolean reuseRefreshTokens;
+
+ private final Duration refreshTokenTimeToLive;
+
+ private final SignatureAlgorithm idTokenSignatureAlgorithm;
+
+ private final boolean x509CertificateBoundAccessTokens;
+
+ public TokenSettings(Duration authorizationCodeTimeToLive, Duration accessTokenTimeToLive, OAuth2TokenFormat accessTokenFormat,
+ Duration deviceCodeTimeToLive, boolean reuseRefreshTokens, Duration refreshTokenTimeToLive, SignatureAlgorithm idTokenSignatureAlgorithm,
+ boolean x509CertificateBoundAccessTokens) {
+ this.authorizationCodeTimeToLive = authorizationCodeTimeToLive;
+ this.accessTokenTimeToLive = accessTokenTimeToLive;
+ this.accessTokenFormat = accessTokenFormat;
+ this.deviceCodeTimeToLive = deviceCodeTimeToLive;
+ this.reuseRefreshTokens = reuseRefreshTokens;
+ this.refreshTokenTimeToLive = refreshTokenTimeToLive;
+ this.idTokenSignatureAlgorithm = idTokenSignatureAlgorithm;
+ this.x509CertificateBoundAccessTokens = x509CertificateBoundAccessTokens;
+ }
+
+ public Duration getAuthorizationCodeTimeToLive() {
+ return this.authorizationCodeTimeToLive;
+ }
+
+ public Duration getAccessTokenTimeToLive() {
+ return this.accessTokenTimeToLive;
+ }
+
+ public OAuth2TokenFormat getAccessTokenFormat() {
+ return this.accessTokenFormat;
+ }
+
+ public Duration getDeviceCodeTimeToLive() {
+ return this.deviceCodeTimeToLive;
+ }
+
+ public boolean isReuseRefreshTokens() {
+ return this.reuseRefreshTokens;
+ }
+
+ public Duration getRefreshTokenTimeToLive() {
+ return this.refreshTokenTimeToLive;
+ }
+
+ public SignatureAlgorithm getIdTokenSignatureAlgorithm() {
+ return this.idTokenSignatureAlgorithm;
+ }
+
+ public boolean isX509CertificateBoundAccessTokens() {
+ return this.x509CertificateBoundAccessTokens;
+ }
+
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2TokenExchangeGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2TokenExchangeGrantAuthorization.java
new file mode 100644
index 00000000..9a054919
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2TokenExchangeGrantAuthorization.java
@@ -0,0 +1,12 @@
+package com.baeldung.redis.entity;
+
+import java.util.Set;
+
+public class OAuth2TokenExchangeGrantAuthorization extends OAuth2AuthorizationGrantAuthorization {
+
+ public OAuth2TokenExchangeGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes,
+ AccessToken accessToken) {
+ super(id, registeredClientId, principalName, authorizedScopes, accessToken, null);
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2UserConsent.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2UserConsent.java
new file mode 100644
index 00000000..684ab2bb
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OAuth2UserConsent.java
@@ -0,0 +1,47 @@
+
+package com.baeldung.redis.entity;
+
+import java.util.Set;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.redis.core.RedisHash;
+import org.springframework.data.redis.core.index.Indexed;
+import org.springframework.security.core.GrantedAuthority;
+
+@RedisHash("oauth2_authorization_consent")
+public class OAuth2UserConsent {
+
+ @Id
+ private final String id;
+
+ @Indexed
+ private final String registeredClientId;
+
+ @Indexed
+ private final String principalName;
+
+ private final Set authorities;
+
+ public OAuth2UserConsent(String id, String registeredClientId, String principalName, Set authorities) {
+ this.id = id;
+ this.registeredClientId = registeredClientId;
+ this.principalName = principalName;
+ this.authorities = authorities;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public String getRegisteredClientId() {
+ return this.registeredClientId;
+ }
+
+ public String getPrincipalName() {
+ return this.principalName;
+ }
+
+ public Set getAuthorities() {
+ return this.authorities;
+ }
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OidcAuthorizationCodeGrantAuthorization.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OidcAuthorizationCodeGrantAuthorization.java
new file mode 100644
index 00000000..a360a226
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/entity/OidcAuthorizationCodeGrantAuthorization.java
@@ -0,0 +1,40 @@
+
+package com.baeldung.redis.entity;
+
+import java.security.Principal;
+import java.time.Instant;
+import java.util.Set;
+
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+
+public class OidcAuthorizationCodeGrantAuthorization extends OAuth2AuthorizationCodeGrantAuthorization {
+
+ private final IdToken idToken;
+
+ public OidcAuthorizationCodeGrantAuthorization(String id, String registeredClientId, String principalName, Set authorizedScopes,
+ AccessToken accessToken, RefreshToken refreshToken, Principal principal, OAuth2AuthorizationRequest authorizationRequest,
+ AuthorizationCode authorizationCode, String state, IdToken idToken) {
+ super(id, registeredClientId, principalName, authorizedScopes, accessToken, refreshToken, principal, authorizationRequest, authorizationCode, state);
+ this.idToken = idToken;
+ }
+
+ public IdToken getIdToken() {
+ return this.idToken;
+ }
+
+ public static class IdToken extends AbstractToken {
+
+ private final ClaimsHolder claims;
+
+ public IdToken(String tokenValue, Instant issuedAt, Instant expiresAt, boolean invalidated, ClaimsHolder claims) {
+ super(tokenValue, issuedAt, expiresAt, invalidated);
+ this.claims = claims;
+ }
+
+ public ClaimsHolder getClaims() {
+ return this.claims;
+ }
+
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2AuthorizationGrantAuthorizationRepository.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2AuthorizationGrantAuthorizationRepository.java
new file mode 100644
index 00000000..0f2f926c
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2AuthorizationGrantAuthorizationRepository.java
@@ -0,0 +1,37 @@
+package com.baeldung.redis.repository;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.redis.entity.OAuth2AuthorizationCodeGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2AuthorizationGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2DeviceCodeGrantAuthorization;
+import com.baeldung.redis.entity.OidcAuthorizationCodeGrantAuthorization;
+
+@Repository
+public interface OAuth2AuthorizationGrantAuthorizationRepository
+ extends CrudRepository {
+
+ T findByState(String state);
+
+ T findByAuthorizationCode_TokenValue(String authorizationCode);
+
+ T findByStateOrAuthorizationCode_TokenValue(String state, String authorizationCode);
+
+ T findByAccessToken_TokenValue(String accessToken);
+
+ T findByRefreshToken_TokenValue(String refreshToken);
+
+ T findByAccessToken_TokenValueOrRefreshToken_TokenValue(String accessToken, String refreshToken);
+
+ T findByIdToken_TokenValue(String idToken);
+
+ T findByDeviceState(String deviceState);
+
+ T findByDeviceCode_TokenValue(String deviceCode);
+
+ T findByUserCode_TokenValue(String userCode);
+
+ T findByDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(String deviceState, String deviceCode, String userCode);
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2RegisteredClientRepository.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2RegisteredClientRepository.java
new file mode 100644
index 00000000..259abcd3
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2RegisteredClientRepository.java
@@ -0,0 +1,13 @@
+package com.baeldung.redis.repository;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.redis.entity.OAuth2RegisteredClient;
+
+@Repository
+public interface OAuth2RegisteredClientRepository extends CrudRepository {
+
+ OAuth2RegisteredClient findByClientId(String clientId);
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2UserConsentRepository.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2UserConsentRepository.java
new file mode 100644
index 00000000..bab7a6f0
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/repository/OAuth2UserConsentRepository.java
@@ -0,0 +1,15 @@
+package com.baeldung.redis.repository;
+
+import org.springframework.data.repository.CrudRepository;
+import org.springframework.stereotype.Repository;
+
+import com.baeldung.redis.entity.OAuth2UserConsent;
+
+@Repository
+public interface OAuth2UserConsentRepository extends CrudRepository {
+
+ OAuth2UserConsent findByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
+
+ void deleteByRegisteredClientIdAndPrincipalName(String registeredClientId, String principalName);
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/ModelMapper.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/ModelMapper.java
new file mode 100644
index 00000000..3fcce02e
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/ModelMapper.java
@@ -0,0 +1,483 @@
+package com.baeldung.redis.service;
+
+import java.security.Principal;
+
+import org.springframework.security.oauth2.core.AuthorizationGrantType;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2DeviceCode;
+import org.springframework.security.oauth2.core.OAuth2RefreshToken;
+import org.springframework.security.oauth2.core.OAuth2UserCode;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.core.oidc.OidcIdToken;
+import org.springframework.security.oauth2.core.oidc.OidcScopes;
+import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
+import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
+import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import com.baeldung.redis.entity.OAuth2AuthorizationCodeGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2AuthorizationGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2ClientCredentialsGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2DeviceCodeGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2RegisteredClient;
+import com.baeldung.redis.entity.OAuth2TokenExchangeGrantAuthorization;
+import com.baeldung.redis.entity.OAuth2UserConsent;
+import com.baeldung.redis.entity.OidcAuthorizationCodeGrantAuthorization;
+
+final class ModelMapper {
+
+ static OAuth2RegisteredClient convertOAuth2RegisteredClient(RegisteredClient registeredClient) {
+ OAuth2RegisteredClient.ClientSettings clientSettings = new OAuth2RegisteredClient.ClientSettings(
+ registeredClient.getClientSettings().isRequireProofKey(),
+ registeredClient.getClientSettings().isRequireAuthorizationConsent(),
+ registeredClient.getClientSettings().getJwkSetUrl(),
+ registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm(),
+ registeredClient.getClientSettings().getX509CertificateSubjectDN());
+
+ OAuth2RegisteredClient.TokenSettings tokenSettings = new OAuth2RegisteredClient.TokenSettings(
+ registeredClient.getTokenSettings().getAuthorizationCodeTimeToLive(),
+ registeredClient.getTokenSettings().getAccessTokenTimeToLive(),
+ registeredClient.getTokenSettings().getAccessTokenFormat(),
+ registeredClient.getTokenSettings().getDeviceCodeTimeToLive(),
+ registeredClient.getTokenSettings().isReuseRefreshTokens(),
+ registeredClient.getTokenSettings().getRefreshTokenTimeToLive(),
+ registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm(),
+ registeredClient.getTokenSettings().isX509CertificateBoundAccessTokens());
+
+ return new OAuth2RegisteredClient(registeredClient.getId(), registeredClient.getClientId(),
+ registeredClient.getClientIdIssuedAt(), registeredClient.getClientSecret(),
+ registeredClient.getClientSecretExpiresAt(), registeredClient.getClientName(),
+ registeredClient.getClientAuthenticationMethods(), registeredClient.getAuthorizationGrantTypes(),
+ registeredClient.getRedirectUris(), registeredClient.getPostLogoutRedirectUris(),
+ registeredClient.getScopes(), clientSettings, tokenSettings);
+ }
+
+ static OAuth2UserConsent convertOAuth2UserConsent(OAuth2AuthorizationConsent authorizationConsent) {
+ String id = authorizationConsent.getRegisteredClientId()
+ .concat("-")
+ .concat(authorizationConsent.getPrincipalName());
+ return new OAuth2UserConsent(id, authorizationConsent.getRegisteredClientId(),
+ authorizationConsent.getPrincipalName(), authorizationConsent.getAuthorities());
+ }
+
+ static OAuth2AuthorizationGrantAuthorization convertOAuth2AuthorizationGrantAuthorization(
+ OAuth2Authorization authorization) {
+
+ if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorization.getAuthorizationGrantType())) {
+ OAuth2AuthorizationRequest authorizationRequest = authorization
+ .getAttribute(OAuth2AuthorizationRequest.class.getName());
+ return authorizationRequest.getScopes().contains(OidcScopes.OPENID)
+ ? convertOidcAuthorizationCodeGrantAuthorization(authorization)
+ : convertOAuth2AuthorizationCodeGrantAuthorization(authorization);
+ }
+ else if (AuthorizationGrantType.CLIENT_CREDENTIALS.equals(authorization.getAuthorizationGrantType())) {
+ return convertOAuth2ClientCredentialsGrantAuthorization(authorization);
+ }
+ else if (AuthorizationGrantType.DEVICE_CODE.equals(authorization.getAuthorizationGrantType())) {
+ return convertOAuth2DeviceCodeGrantAuthorization(authorization);
+ }
+ else if (AuthorizationGrantType.TOKEN_EXCHANGE.equals(authorization.getAuthorizationGrantType())) {
+ return convertOAuth2TokenExchangeGrantAuthorization(authorization);
+ }
+ return null;
+ }
+
+ static OidcAuthorizationCodeGrantAuthorization convertOidcAuthorizationCodeGrantAuthorization(
+ OAuth2Authorization authorization) {
+ OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode authorizationCode = extractAuthorizationCode(
+ authorization);
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = extractAccessToken(authorization);
+ OAuth2AuthorizationGrantAuthorization.RefreshToken refreshToken = extractRefreshToken(authorization);
+ OidcAuthorizationCodeGrantAuthorization.IdToken idToken = extractIdToken(authorization);
+
+ return new OidcAuthorizationCodeGrantAuthorization(authorization.getId(), authorization.getRegisteredClientId(),
+ authorization.getPrincipalName(), authorization.getAuthorizedScopes(), accessToken, refreshToken,
+ authorization.getAttribute(Principal.class.getName()),
+ authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()), authorizationCode,
+ authorization.getAttribute(OAuth2ParameterNames.STATE), idToken);
+ }
+
+ static OAuth2AuthorizationCodeGrantAuthorization convertOAuth2AuthorizationCodeGrantAuthorization(
+ OAuth2Authorization authorization) {
+
+ OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode authorizationCode = extractAuthorizationCode(
+ authorization);
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = extractAccessToken(authorization);
+ OAuth2AuthorizationGrantAuthorization.RefreshToken refreshToken = extractRefreshToken(authorization);
+
+ return new OAuth2AuthorizationCodeGrantAuthorization(authorization.getId(),
+ authorization.getRegisteredClientId(), authorization.getPrincipalName(),
+ authorization.getAuthorizedScopes(), accessToken, refreshToken,
+ authorization.getAttribute(Principal.class.getName()),
+ authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()), authorizationCode,
+ authorization.getAttribute(OAuth2ParameterNames.STATE));
+ }
+
+ static OAuth2ClientCredentialsGrantAuthorization convertOAuth2ClientCredentialsGrantAuthorization(
+ OAuth2Authorization authorization) {
+
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = extractAccessToken(authorization);
+
+ return new OAuth2ClientCredentialsGrantAuthorization(authorization.getId(),
+ authorization.getRegisteredClientId(), authorization.getPrincipalName(),
+ authorization.getAuthorizedScopes(), accessToken);
+ }
+
+ static OAuth2DeviceCodeGrantAuthorization convertOAuth2DeviceCodeGrantAuthorization(
+ OAuth2Authorization authorization) {
+
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = extractAccessToken(authorization);
+ OAuth2AuthorizationGrantAuthorization.RefreshToken refreshToken = extractRefreshToken(authorization);
+ OAuth2DeviceCodeGrantAuthorization.DeviceCode deviceCode = extractDeviceCode(authorization);
+ OAuth2DeviceCodeGrantAuthorization.UserCode userCode = extractUserCode(authorization);
+
+ return new OAuth2DeviceCodeGrantAuthorization(authorization.getId(), authorization.getRegisteredClientId(),
+ authorization.getPrincipalName(), authorization.getAuthorizedScopes(), accessToken, refreshToken,
+ authorization.getAttribute(Principal.class.getName()), deviceCode, userCode,
+ authorization.getAttribute(OAuth2ParameterNames.SCOPE),
+ authorization.getAttribute(OAuth2ParameterNames.STATE));
+ }
+
+ static OAuth2TokenExchangeGrantAuthorization convertOAuth2TokenExchangeGrantAuthorization(
+ OAuth2Authorization authorization) {
+
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = extractAccessToken(authorization);
+
+ return new OAuth2TokenExchangeGrantAuthorization(authorization.getId(), authorization.getRegisteredClientId(),
+ authorization.getPrincipalName(), authorization.getAuthorizedScopes(), accessToken);
+ }
+
+ static OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode extractAuthorizationCode(
+ OAuth2Authorization authorization) {
+ OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode authorizationCode = null;
+ if (authorization.getToken(OAuth2AuthorizationCode.class) != null) {
+ OAuth2Authorization.Token oauth2AuthorizationCode = authorization
+ .getToken(OAuth2AuthorizationCode.class);
+ authorizationCode = new OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode(
+ oauth2AuthorizationCode.getToken().getTokenValue(),
+ oauth2AuthorizationCode.getToken().getIssuedAt(), oauth2AuthorizationCode.getToken().getExpiresAt(),
+ oauth2AuthorizationCode.isInvalidated());
+ }
+ return authorizationCode;
+ }
+
+ static OAuth2AuthorizationGrantAuthorization.AccessToken extractAccessToken(OAuth2Authorization authorization) {
+ OAuth2AuthorizationGrantAuthorization.AccessToken accessToken = null;
+ if (authorization.getAccessToken() != null) {
+ OAuth2Authorization.Token oauth2AccessToken = authorization.getAccessToken();
+ OAuth2TokenFormat tokenFormat = null;
+ if (OAuth2TokenFormat.SELF_CONTAINED.getValue()
+ .equals(oauth2AccessToken.getMetadata(OAuth2TokenFormat.class.getName()))) {
+ tokenFormat = OAuth2TokenFormat.SELF_CONTAINED;
+ }
+ else if (OAuth2TokenFormat.REFERENCE.getValue()
+ .equals(oauth2AccessToken.getMetadata(OAuth2TokenFormat.class.getName()))) {
+ tokenFormat = OAuth2TokenFormat.REFERENCE;
+ }
+ accessToken = new OAuth2AuthorizationGrantAuthorization.AccessToken(
+ oauth2AccessToken.getToken().getTokenValue(), oauth2AccessToken.getToken().getIssuedAt(),
+ oauth2AccessToken.getToken().getExpiresAt(), oauth2AccessToken.isInvalidated(),
+ oauth2AccessToken.getToken().getTokenType(), oauth2AccessToken.getToken().getScopes(), tokenFormat,
+ new OAuth2AuthorizationGrantAuthorization.ClaimsHolder(oauth2AccessToken.getClaims()));
+ }
+ return accessToken;
+ }
+
+ static OAuth2AuthorizationGrantAuthorization.RefreshToken extractRefreshToken(OAuth2Authorization authorization) {
+ OAuth2AuthorizationGrantAuthorization.RefreshToken refreshToken = null;
+ if (authorization.getRefreshToken() != null) {
+ OAuth2Authorization.Token oauth2RefreshToken = authorization.getRefreshToken();
+ refreshToken = new OAuth2AuthorizationGrantAuthorization.RefreshToken(
+ oauth2RefreshToken.getToken().getTokenValue(), oauth2RefreshToken.getToken().getIssuedAt(),
+ oauth2RefreshToken.getToken().getExpiresAt(), oauth2RefreshToken.isInvalidated());
+ }
+ return refreshToken;
+ }
+
+ static OidcAuthorizationCodeGrantAuthorization.IdToken extractIdToken(OAuth2Authorization authorization) {
+ OidcAuthorizationCodeGrantAuthorization.IdToken idToken = null;
+ if (authorization.getToken(OidcIdToken.class) != null) {
+ OAuth2Authorization.Token oidcIdToken = authorization.getToken(OidcIdToken.class);
+ idToken = new OidcAuthorizationCodeGrantAuthorization.IdToken(oidcIdToken.getToken().getTokenValue(),
+ oidcIdToken.getToken().getIssuedAt(), oidcIdToken.getToken().getExpiresAt(),
+ oidcIdToken.isInvalidated(),
+ new OAuth2AuthorizationGrantAuthorization.ClaimsHolder(oidcIdToken.getClaims()));
+ }
+ return idToken;
+ }
+
+ static OAuth2DeviceCodeGrantAuthorization.DeviceCode extractDeviceCode(OAuth2Authorization authorization) {
+ OAuth2DeviceCodeGrantAuthorization.DeviceCode deviceCode = null;
+ if (authorization.getToken(OAuth2DeviceCode.class) != null) {
+ OAuth2Authorization.Token oauth2DeviceCode = authorization
+ .getToken(OAuth2DeviceCode.class);
+ deviceCode = new OAuth2DeviceCodeGrantAuthorization.DeviceCode(oauth2DeviceCode.getToken().getTokenValue(),
+ oauth2DeviceCode.getToken().getIssuedAt(), oauth2DeviceCode.getToken().getExpiresAt(),
+ oauth2DeviceCode.isInvalidated());
+ }
+ return deviceCode;
+ }
+
+ static OAuth2DeviceCodeGrantAuthorization.UserCode extractUserCode(OAuth2Authorization authorization) {
+ OAuth2DeviceCodeGrantAuthorization.UserCode userCode = null;
+ if (authorization.getToken(OAuth2UserCode.class) != null) {
+ OAuth2Authorization.Token oauth2UserCode = authorization.getToken(OAuth2UserCode.class);
+ userCode = new OAuth2DeviceCodeGrantAuthorization.UserCode(oauth2UserCode.getToken().getTokenValue(),
+ oauth2UserCode.getToken().getIssuedAt(), oauth2UserCode.getToken().getExpiresAt(),
+ oauth2UserCode.isInvalidated());
+ }
+ return userCode;
+ }
+
+ static RegisteredClient convertRegisteredClient(OAuth2RegisteredClient oauth2RegisteredClient) {
+ ClientSettings.Builder clientSettingsBuilder = ClientSettings.builder()
+ .requireProofKey(oauth2RegisteredClient.getClientSettings().isRequireProofKey())
+ .requireAuthorizationConsent(oauth2RegisteredClient.getClientSettings().isRequireAuthorizationConsent());
+ if (StringUtils.hasText(oauth2RegisteredClient.getClientSettings().getJwkSetUrl())) {
+ clientSettingsBuilder.jwkSetUrl(oauth2RegisteredClient.getClientSettings().getJwkSetUrl());
+ }
+ if (oauth2RegisteredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm() != null) {
+ clientSettingsBuilder.tokenEndpointAuthenticationSigningAlgorithm(
+ oauth2RegisteredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm());
+ }
+ if (StringUtils.hasText(oauth2RegisteredClient.getClientSettings().getX509CertificateSubjectDN())) {
+ clientSettingsBuilder
+ .x509CertificateSubjectDN(oauth2RegisteredClient.getClientSettings().getX509CertificateSubjectDN());
+ }
+ ClientSettings clientSettings = clientSettingsBuilder.build();
+
+ TokenSettings.Builder tokenSettingsBuilder = TokenSettings.builder();
+ if (oauth2RegisteredClient.getTokenSettings().getAuthorizationCodeTimeToLive() != null) {
+ tokenSettingsBuilder.authorizationCodeTimeToLive(
+ oauth2RegisteredClient.getTokenSettings().getAuthorizationCodeTimeToLive());
+ }
+ if (oauth2RegisteredClient.getTokenSettings().getAccessTokenTimeToLive() != null) {
+ tokenSettingsBuilder
+ .accessTokenTimeToLive(oauth2RegisteredClient.getTokenSettings().getAccessTokenTimeToLive());
+ }
+ if (oauth2RegisteredClient.getTokenSettings().getAccessTokenFormat() != null) {
+ tokenSettingsBuilder.accessTokenFormat(oauth2RegisteredClient.getTokenSettings().getAccessTokenFormat());
+ }
+ if (oauth2RegisteredClient.getTokenSettings().getDeviceCodeTimeToLive() != null) {
+ tokenSettingsBuilder
+ .deviceCodeTimeToLive(oauth2RegisteredClient.getTokenSettings().getDeviceCodeTimeToLive());
+ }
+ tokenSettingsBuilder.reuseRefreshTokens(oauth2RegisteredClient.getTokenSettings().isReuseRefreshTokens());
+ if (oauth2RegisteredClient.getTokenSettings().getRefreshTokenTimeToLive() != null) {
+ tokenSettingsBuilder
+ .refreshTokenTimeToLive(oauth2RegisteredClient.getTokenSettings().getRefreshTokenTimeToLive());
+ }
+ if (oauth2RegisteredClient.getTokenSettings().getIdTokenSignatureAlgorithm() != null) {
+ tokenSettingsBuilder
+ .idTokenSignatureAlgorithm(oauth2RegisteredClient.getTokenSettings().getIdTokenSignatureAlgorithm());
+ }
+ tokenSettingsBuilder.x509CertificateBoundAccessTokens(
+ oauth2RegisteredClient.getTokenSettings().isX509CertificateBoundAccessTokens());
+ TokenSettings tokenSettings = tokenSettingsBuilder.build();
+
+ RegisteredClient.Builder registeredClientBuilder = RegisteredClient.withId(oauth2RegisteredClient.getId())
+ .clientId(oauth2RegisteredClient.getClientId())
+ .clientIdIssuedAt(oauth2RegisteredClient.getClientIdIssuedAt())
+ .clientSecret(oauth2RegisteredClient.getClientSecret())
+ .clientSecretExpiresAt(oauth2RegisteredClient.getClientSecretExpiresAt())
+ .clientName(oauth2RegisteredClient.getClientName())
+ .clientAuthenticationMethods((clientAuthenticationMethods) -> clientAuthenticationMethods
+ .addAll(oauth2RegisteredClient.getClientAuthenticationMethods()))
+ .authorizationGrantTypes((authorizationGrantTypes) -> authorizationGrantTypes
+ .addAll(oauth2RegisteredClient.getAuthorizationGrantTypes()))
+ .clientSettings(clientSettings)
+ .tokenSettings(tokenSettings);
+ if (!CollectionUtils.isEmpty(oauth2RegisteredClient.getRedirectUris())) {
+ registeredClientBuilder.redirectUris((redirectUris) -> redirectUris.addAll(oauth2RegisteredClient.getRedirectUris()));
+ }
+ if (!CollectionUtils.isEmpty(oauth2RegisteredClient.getPostLogoutRedirectUris())) {
+ registeredClientBuilder.postLogoutRedirectUris((postLogoutRedirectUris) ->
+ postLogoutRedirectUris.addAll(oauth2RegisteredClient.getPostLogoutRedirectUris()));
+ }
+ if (!CollectionUtils.isEmpty(oauth2RegisteredClient.getScopes())) {
+ registeredClientBuilder.scopes((scopes) -> scopes.addAll(oauth2RegisteredClient.getScopes()));
+ }
+
+ return registeredClientBuilder.build();
+ }
+
+ static OAuth2AuthorizationConsent convertOAuth2AuthorizationConsent(OAuth2UserConsent userConsent) {
+ return OAuth2AuthorizationConsent.withId(userConsent.getRegisteredClientId(), userConsent.getPrincipalName())
+ .authorities((authorities) -> authorities.addAll(userConsent.getAuthorities()))
+ .build();
+ }
+
+ static void mapOAuth2AuthorizationGrantAuthorization(
+ OAuth2AuthorizationGrantAuthorization authorizationGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ if (authorizationGrantAuthorization instanceof OidcAuthorizationCodeGrantAuthorization authorizationGrant) {
+ mapOidcAuthorizationCodeGrantAuthorization(authorizationGrant, builder);
+ }
+ else if (authorizationGrantAuthorization instanceof OAuth2AuthorizationCodeGrantAuthorization authorizationGrant) {
+ mapOAuth2AuthorizationCodeGrantAuthorization(authorizationGrant, builder);
+ }
+ else if (authorizationGrantAuthorization instanceof OAuth2ClientCredentialsGrantAuthorization authorizationGrant) {
+ mapOAuth2ClientCredentialsGrantAuthorization(authorizationGrant, builder);
+ }
+ else if (authorizationGrantAuthorization instanceof OAuth2DeviceCodeGrantAuthorization authorizationGrant) {
+ mapOAuth2DeviceCodeGrantAuthorization(authorizationGrant, builder);
+ }
+ else if (authorizationGrantAuthorization instanceof OAuth2TokenExchangeGrantAuthorization authorizationGrant) {
+ mapOAuth2TokenExchangeGrantAuthorization(authorizationGrant, builder);
+ }
+ }
+
+ static void mapOidcAuthorizationCodeGrantAuthorization(
+ OidcAuthorizationCodeGrantAuthorization authorizationCodeGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ mapOAuth2AuthorizationCodeGrantAuthorization(authorizationCodeGrantAuthorization, builder);
+ mapIdToken(authorizationCodeGrantAuthorization.getIdToken(), builder);
+ }
+
+ static void mapOAuth2AuthorizationCodeGrantAuthorization(
+ OAuth2AuthorizationCodeGrantAuthorization authorizationCodeGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ builder.id(authorizationCodeGrantAuthorization.getId())
+ .principalName(authorizationCodeGrantAuthorization.getPrincipalName())
+ .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
+ .authorizedScopes(authorizationCodeGrantAuthorization.getAuthorizedScopes())
+ .attribute(Principal.class.getName(), authorizationCodeGrantAuthorization.getPrincipal())
+ .attribute(OAuth2AuthorizationRequest.class.getName(),
+ authorizationCodeGrantAuthorization.getAuthorizationRequest());
+ if (StringUtils.hasText(authorizationCodeGrantAuthorization.getState())) {
+ builder.attribute(OAuth2ParameterNames.STATE, authorizationCodeGrantAuthorization.getState());
+ }
+
+ mapAuthorizationCode(authorizationCodeGrantAuthorization.getAuthorizationCode(), builder);
+ mapAccessToken(authorizationCodeGrantAuthorization.getAccessToken(), builder);
+ mapRefreshToken(authorizationCodeGrantAuthorization.getRefreshToken(), builder);
+ }
+
+ static void mapOAuth2ClientCredentialsGrantAuthorization(
+ OAuth2ClientCredentialsGrantAuthorization clientCredentialsGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ builder.id(clientCredentialsGrantAuthorization.getId())
+ .principalName(clientCredentialsGrantAuthorization.getPrincipalName())
+ .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
+ .authorizedScopes(clientCredentialsGrantAuthorization.getAuthorizedScopes());
+
+ mapAccessToken(clientCredentialsGrantAuthorization.getAccessToken(), builder);
+ }
+
+ static void mapOAuth2DeviceCodeGrantAuthorization(OAuth2DeviceCodeGrantAuthorization deviceCodeGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ builder.id(deviceCodeGrantAuthorization.getId())
+ .principalName(deviceCodeGrantAuthorization.getPrincipalName())
+ .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE)
+ .authorizedScopes(deviceCodeGrantAuthorization.getAuthorizedScopes());
+ if (deviceCodeGrantAuthorization.getPrincipal() != null) {
+ builder.attribute(Principal.class.getName(), deviceCodeGrantAuthorization.getPrincipal());
+ }
+ if (deviceCodeGrantAuthorization.getRequestedScopes() != null) {
+ builder.attribute(OAuth2ParameterNames.SCOPE, deviceCodeGrantAuthorization.getRequestedScopes());
+ }
+ if (StringUtils.hasText(deviceCodeGrantAuthorization.getDeviceState())) {
+ builder.attribute(OAuth2ParameterNames.STATE, deviceCodeGrantAuthorization.getDeviceState());
+ }
+
+ mapAccessToken(deviceCodeGrantAuthorization.getAccessToken(), builder);
+ mapRefreshToken(deviceCodeGrantAuthorization.getRefreshToken(), builder);
+ mapDeviceCode(deviceCodeGrantAuthorization.getDeviceCode(), builder);
+ mapUserCode(deviceCodeGrantAuthorization.getUserCode(), builder);
+ }
+
+ static void mapOAuth2TokenExchangeGrantAuthorization(
+ OAuth2TokenExchangeGrantAuthorization tokenExchangeGrantAuthorization,
+ OAuth2Authorization.Builder builder) {
+
+ builder.id(tokenExchangeGrantAuthorization.getId())
+ .principalName(tokenExchangeGrantAuthorization.getPrincipalName())
+ .authorizationGrantType(AuthorizationGrantType.TOKEN_EXCHANGE)
+ .authorizedScopes(tokenExchangeGrantAuthorization.getAuthorizedScopes());
+
+ mapAccessToken(tokenExchangeGrantAuthorization.getAccessToken(), builder);
+ }
+
+ static void mapAuthorizationCode(OAuth2AuthorizationCodeGrantAuthorization.AuthorizationCode authorizationCode,
+ OAuth2Authorization.Builder builder) {
+ if (authorizationCode == null) {
+ return;
+ }
+ OAuth2AuthorizationCode oauth2AuthorizationCode = new OAuth2AuthorizationCode(authorizationCode.getTokenValue(),
+ authorizationCode.getIssuedAt(), authorizationCode.getExpiresAt());
+ builder.token(oauth2AuthorizationCode, (metadata) -> metadata
+ .put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, authorizationCode.isInvalidated()));
+ }
+
+ static void mapAccessToken(OAuth2AuthorizationGrantAuthorization.AccessToken accessToken,
+ OAuth2Authorization.Builder builder) {
+ if (accessToken == null) {
+ return;
+ }
+ OAuth2AccessToken oauth2AccessToken = new OAuth2AccessToken(accessToken.getTokenType(),
+ accessToken.getTokenValue(), accessToken.getIssuedAt(), accessToken.getExpiresAt(),
+ accessToken.getScopes());
+ builder.token(oauth2AccessToken, (metadata) -> {
+ metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, accessToken.isInvalidated());
+ metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, accessToken.getClaims().getClaims());
+ metadata.put(OAuth2TokenFormat.class.getName(), accessToken.getTokenFormat().getValue());
+ });
+ }
+
+ static void mapRefreshToken(OAuth2AuthorizationGrantAuthorization.RefreshToken refreshToken,
+ OAuth2Authorization.Builder builder) {
+ if (refreshToken == null) {
+ return;
+ }
+ OAuth2RefreshToken oauth2RefreshToken = new OAuth2RefreshToken(refreshToken.getTokenValue(),
+ refreshToken.getIssuedAt(), refreshToken.getExpiresAt());
+ builder.token(oauth2RefreshToken, (metadata) -> metadata
+ .put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, refreshToken.isInvalidated()));
+ }
+
+ static void mapIdToken(OidcAuthorizationCodeGrantAuthorization.IdToken idToken,
+ OAuth2Authorization.Builder builder) {
+ if (idToken == null) {
+ return;
+ }
+ OidcIdToken oidcIdToken = new OidcIdToken(idToken.getTokenValue(), idToken.getIssuedAt(),
+ idToken.getExpiresAt(), idToken.getClaims().getClaims());
+ builder.token(oidcIdToken, (metadata) -> {
+ metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, idToken.isInvalidated());
+ metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims().getClaims());
+ });
+ }
+
+ static void mapDeviceCode(OAuth2DeviceCodeGrantAuthorization.DeviceCode deviceCode,
+ OAuth2Authorization.Builder builder) {
+ if (deviceCode == null) {
+ return;
+ }
+ OAuth2DeviceCode oauth2DeviceCode = new OAuth2DeviceCode(deviceCode.getTokenValue(), deviceCode.getIssuedAt(),
+ deviceCode.getExpiresAt());
+ builder.token(oauth2DeviceCode, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME,
+ deviceCode.isInvalidated()));
+ }
+
+ static void mapUserCode(OAuth2DeviceCodeGrantAuthorization.UserCode userCode, OAuth2Authorization.Builder builder) {
+ if (userCode == null) {
+ return;
+ }
+ OAuth2UserCode oauth2UserCode = new OAuth2UserCode(userCode.getTokenValue(), userCode.getIssuedAt(),
+ userCode.getExpiresAt());
+ builder.token(oauth2UserCode, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME,
+ userCode.isInvalidated()));
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationConsentService.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationConsentService.java
new file mode 100644
index 00000000..b727dd1e
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationConsentService.java
@@ -0,0 +1,43 @@
+package com.baeldung.redis.service;
+
+import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
+import org.springframework.util.Assert;
+
+import com.baeldung.redis.entity.OAuth2UserConsent;
+import com.baeldung.redis.repository.OAuth2UserConsentRepository;
+
+public class RedisOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
+
+ private final OAuth2UserConsentRepository userConsentRepository;
+
+ public RedisOAuth2AuthorizationConsentService(OAuth2UserConsentRepository userConsentRepository) {
+ Assert.notNull(userConsentRepository, "userConsentRepository cannot be null");
+ this.userConsentRepository = userConsentRepository;
+ }
+
+ @Override
+ public void save(OAuth2AuthorizationConsent authorizationConsent) {
+ Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
+ OAuth2UserConsent oauth2UserConsent = ModelMapper.convertOAuth2UserConsent(authorizationConsent);
+ this.userConsentRepository.save(oauth2UserConsent);
+ }
+
+ @Override
+ public void remove(OAuth2AuthorizationConsent authorizationConsent) {
+ Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
+ this.userConsentRepository.deleteByRegisteredClientIdAndPrincipalName(authorizationConsent.getRegisteredClientId(), authorizationConsent
+ .getPrincipalName());
+ }
+
+ @Nullable
+ @Override
+ public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) {
+ Assert.hasText(registeredClientId, "registeredClientId cannot be empty");
+ Assert.hasText(principalName, "principalName cannot be empty");
+ OAuth2UserConsent oauth2UserConsent = this.userConsentRepository.findByRegisteredClientIdAndPrincipalName(registeredClientId, principalName);
+ return oauth2UserConsent != null ? ModelMapper.convertOAuth2AuthorizationConsent(oauth2UserConsent) : null;
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationService.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationService.java
new file mode 100644
index 00000000..f1aa290a
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisOAuth2AuthorizationService.java
@@ -0,0 +1,98 @@
+package com.baeldung.redis.service;
+
+import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
+import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
+import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
+import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
+import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.springframework.util.Assert;
+
+import com.baeldung.redis.entity.OAuth2AuthorizationGrantAuthorization;
+import com.baeldung.redis.repository.OAuth2AuthorizationGrantAuthorizationRepository;
+
+public class RedisOAuth2AuthorizationService implements OAuth2AuthorizationService {
+
+ private final RegisteredClientRepository registeredClientRepository;
+
+ private final OAuth2AuthorizationGrantAuthorizationRepository authorizationGrantAuthorizationRepository;
+
+ public RedisOAuth2AuthorizationService(RegisteredClientRepository registeredClientRepository,
+ OAuth2AuthorizationGrantAuthorizationRepository authorizationGrantAuthorizationRepository) {
+ Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
+ Assert.notNull(authorizationGrantAuthorizationRepository, "authorizationGrantAuthorizationRepository cannot be null");
+ this.registeredClientRepository = registeredClientRepository;
+ this.authorizationGrantAuthorizationRepository = authorizationGrantAuthorizationRepository;
+ }
+
+ @Override
+ public void save(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+ OAuth2AuthorizationGrantAuthorization authorizationGrantAuthorization = ModelMapper.convertOAuth2AuthorizationGrantAuthorization(authorization);
+ this.authorizationGrantAuthorizationRepository.save(authorizationGrantAuthorization);
+ }
+
+ @Override
+ public void remove(OAuth2Authorization authorization) {
+ Assert.notNull(authorization, "authorization cannot be null");
+ this.authorizationGrantAuthorizationRepository.deleteById(authorization.getId());
+ }
+
+ @Nullable
+ @Override
+ public OAuth2Authorization findById(String id) {
+ Assert.hasText(id, "id cannot be empty");
+ return this.authorizationGrantAuthorizationRepository.findById(id)
+ .map(this::toOAuth2Authorization)
+ .orElse(null);
+ }
+
+ @Nullable
+ @Override
+ public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {
+ Assert.hasText(token, "token cannot be empty");
+ OAuth2AuthorizationGrantAuthorization authorizationGrantAuthorization = null;
+ if (tokenType == null) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByStateOrAuthorizationCode_TokenValue(token, token);
+ if (authorizationGrantAuthorization == null) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByAccessToken_TokenValueOrRefreshToken_TokenValue(token,
+ token);
+ }
+ if (authorizationGrantAuthorization == null) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByIdToken_TokenValue(token);
+ }
+ if (authorizationGrantAuthorization == null) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByDeviceStateOrDeviceCode_TokenValueOrUserCode_TokenValue(
+ token, token, token);
+ }
+ } else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByState(token);
+ if (authorizationGrantAuthorization == null) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByDeviceState(token);
+ }
+ } else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByAuthorizationCode_TokenValue(token);
+ } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByAccessToken_TokenValue(token);
+ } else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByIdToken_TokenValue(token);
+ } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByRefreshToken_TokenValue(token);
+ } else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByUserCode_TokenValue(token);
+ } else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {
+ authorizationGrantAuthorization = this.authorizationGrantAuthorizationRepository.findByDeviceCode_TokenValue(token);
+ }
+ return authorizationGrantAuthorization != null ? toOAuth2Authorization(authorizationGrantAuthorization) : null;
+ }
+
+ private OAuth2Authorization toOAuth2Authorization(OAuth2AuthorizationGrantAuthorization authorizationGrantAuthorization) {
+ RegisteredClient registeredClient = this.registeredClientRepository.findById(authorizationGrantAuthorization.getRegisteredClientId());
+ OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient);
+ ModelMapper.mapOAuth2AuthorizationGrantAuthorization(authorizationGrantAuthorization, builder);
+ return builder.build();
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisRegisteredClientRepository.java b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisRegisteredClientRepository.java
new file mode 100644
index 00000000..db41f89b
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/java/com/baeldung/redis/service/RedisRegisteredClientRepository.java
@@ -0,0 +1,44 @@
+package com.baeldung.redis.service;
+
+import org.springframework.lang.Nullable;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
+import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
+import org.springframework.util.Assert;
+
+import com.baeldung.redis.entity.OAuth2RegisteredClient;
+import com.baeldung.redis.repository.OAuth2RegisteredClientRepository;
+
+public class RedisRegisteredClientRepository implements RegisteredClientRepository {
+
+ private final OAuth2RegisteredClientRepository registeredClientRepository;
+
+ public RedisRegisteredClientRepository(OAuth2RegisteredClientRepository registeredClientRepository) {
+ Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
+ this.registeredClientRepository = registeredClientRepository;
+ }
+
+ @Override
+ public void save(RegisteredClient registeredClient) {
+ Assert.notNull(registeredClient, "registeredClient cannot be null");
+ OAuth2RegisteredClient oauth2RegisteredClient = ModelMapper.convertOAuth2RegisteredClient(registeredClient);
+ this.registeredClientRepository.save(oauth2RegisteredClient);
+ }
+
+ @Nullable
+ @Override
+ public RegisteredClient findById(String id) {
+ Assert.hasText(id, "id cannot be empty");
+ return this.registeredClientRepository.findById(id)
+ .map(ModelMapper::convertRegisteredClient)
+ .orElse(null);
+ }
+
+ @Nullable
+ @Override
+ public RegisteredClient findByClientId(String clientId) {
+ Assert.hasText(clientId, "clientId cannot be empty");
+ OAuth2RegisteredClient oauth2RegisteredClient = this.registeredClientRepository.findByClientId(clientId);
+ return oauth2RegisteredClient != null ? ModelMapper.convertRegisteredClient(oauth2RegisteredClient) : null;
+ }
+
+}
diff --git a/redis-authorization-server/redis-oauth-server/src/main/resources/application.yml b/redis-authorization-server/redis-oauth-server/src/main/resources/application.yml
new file mode 100644
index 00000000..ca8cbeef
--- /dev/null
+++ b/redis-authorization-server/redis-oauth-server/src/main/resources/application.yml
@@ -0,0 +1,16 @@
+server:
+ port: 9000
+
+logging:
+ level:
+ root: INFO
+ org.springframework.web: INFO
+ org.springframework.security: INFO
+ org.springframework.security.oauth2: INFO
+ org.springframework.boot: INFO
+
+spring:
+ security:
+ oauth2:
+ authorizationserver:
+ issuer: http://localhost:9000
\ No newline at end of file
diff --git a/redis-authorization-server/redis-resource-server/.gitignore b/redis-authorization-server/redis-resource-server/.gitignore
new file mode 100644
index 00000000..0449f146
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/.gitignore
@@ -0,0 +1,4 @@
+/.classpath
+/.project
+/target/
+/.settings/
diff --git a/redis-authorization-server/redis-resource-server/pom.xml b/redis-authorization-server/redis-resource-server/pom.xml
new file mode 100644
index 00000000..ae16d32c
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/pom.xml
@@ -0,0 +1,62 @@
+
+ 4.0.0
+
+ com.baeldung
+ redis-resource-server
+ redis-resource-server
+ 0.1.0-SNAPSHOT
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.4.3
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
+ eugen@baeldung.com
+ Eugen Paraschiv
+ https://github.com/eugenp
+ eugenp
+
+
+
+
+ UTF-8
+ 17
+
+
+
diff --git a/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/ResourceServerApplication.java b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/ResourceServerApplication.java
new file mode 100644
index 00000000..0004de6c
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/ResourceServerApplication.java
@@ -0,0 +1,12 @@
+package com.baeldung.resource;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class ResourceServerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ResourceServerApplication.class, args);
+ }
+}
diff --git a/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/config/ResourceServerConfig.java b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/config/ResourceServerConfig.java
new file mode 100644
index 00000000..74923360
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/config/ResourceServerConfig.java
@@ -0,0 +1,22 @@
+package com.baeldung.resource.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class ResourceServerConfig {
+
+ @Bean
+ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http.securityMatcher("/articles/**")
+ .authorizeHttpRequests(authorize -> authorize.anyRequest()
+ .hasAuthority("SCOPE_articles.read"))
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
+ return http.build();
+ }
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/web/ArticlesController.java b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/web/ArticlesController.java
new file mode 100644
index 00000000..10c58fe7
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/src/main/java/com/baeldung/resource/web/ArticlesController.java
@@ -0,0 +1,13 @@
+package com.baeldung.resource.web;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class ArticlesController {
+
+ @GetMapping("/articles")
+ public String[] getArticles() {
+ return new String[]{"Article 1", "Article 2", "Article 3"};
+ }
+}
\ No newline at end of file
diff --git a/redis-authorization-server/redis-resource-server/src/main/resources/application.yml b/redis-authorization-server/redis-resource-server/src/main/resources/application.yml
new file mode 100644
index 00000000..1b76f268
--- /dev/null
+++ b/redis-authorization-server/redis-resource-server/src/main/resources/application.yml
@@ -0,0 +1,18 @@
+
+server:
+ port: 8090
+
+logging:
+ level:
+ root: INFO
+ org.springframework.web: INFO
+ org.springframework.security: INFO
+ org.springframework.security.oauth2: INFO
+ org.springframework.boot: INFO
+
+spring:
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ issuer-uri: http://localhost:9000
\ No newline at end of file