diff --git a/docs/modules/ROOT/pages/core-model-components.adoc b/docs/modules/ROOT/pages/core-model-components.adoc index 03032ce4..c6fe2e6d 100644 --- a/docs/modules/ROOT/pages/core-model-components.adoc +++ b/docs/modules/ROOT/pages/core-model-components.adoc @@ -393,6 +393,7 @@ The following example shows how to register an `OAuth2TokenGenerator` `@Bean`: public OAuth2TokenGenerator tokenGenerator() { JwtEncoder jwtEncoder = ... JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); return new DelegatingOAuth2TokenGenerator( @@ -441,6 +442,7 @@ The following example shows how to implement an `OAuth2TokenCustomizer tokenGenerator() { JwtEncoder jwtEncoder = ... JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer()); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); diff --git a/docs/src/main/java/sample/extgrant/SecurityConfig.java b/docs/src/main/java/sample/extgrant/SecurityConfig.java index 1044363b..4424e6ce 100644 --- a/docs/src/main/java/sample/extgrant/SecurityConfig.java +++ b/docs/src/main/java/sample/extgrant/SecurityConfig.java @@ -15,6 +15,7 @@ */ package sample.extgrant; +import java.time.Clock; import java.util.UUID; import com.nimbusds.jose.jwk.source.JWKSource; @@ -100,6 +101,7 @@ public class SecurityConfig { @Bean OAuth2TokenGenerator tokenGenerator(JWKSource jwkSource) { JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource)); + jwtGenerator.setClock(Clock.systemUTC()); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); return new DelegatingOAuth2TokenGenerator( diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java index dd9bf859..f8d5c2a7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; +import java.time.Clock; import java.util.Map; import com.nimbusds.jose.jwk.source.JWKSource; @@ -128,6 +129,7 @@ final class OAuth2ConfigurerUtils { JwtEncoder jwtEncoder = getJwtEncoder(httpSecurity); if (jwtEncoder != null) { jwtGenerator = new JwtGenerator(jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(getJwtCustomizer(httpSecurity)); httpSecurity.setSharedObject(JwtGenerator.class, jwtGenerator); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java index 249c86e3..cbe46671 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.token; +import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -61,6 +62,7 @@ import org.springframework.util.StringUtils; public final class JwtGenerator implements OAuth2TokenGenerator { private final JwtEncoder jwtEncoder; + private Clock clock = Clock.systemUTC(); private OAuth2TokenCustomizer jwtCustomizer; @@ -94,7 +96,7 @@ public final class JwtGenerator implements OAuth2TokenGenerator { } RegisteredClient registeredClient = context.getRegisteredClient(); - Instant issuedAt = Instant.now(); + Instant issuedAt = clock.instant(); Instant expiresAt; JwsAlgorithm jwsAlgorithm = SignatureAlgorithm.RS256; if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { @@ -207,4 +209,19 @@ public final class JwtGenerator implements OAuth2TokenGenerator { this.jwtCustomizer = jwtCustomizer; } + /** + * Sets the {@link Clock} to be used by this component. + *

+ * The provided clock must not be {@code null}. + * This allows injecting a custom clock for testing or + * adjusting time-related behavior. + * + * @param clock the {@link Clock} instance to set, must not be {@code null} + * @throws IllegalArgumentException if {@code clock} is {@code null} + */ + public void setClock(Clock clock) { + Assert.notNull(clock, "Clock must not be null"); + this.clock = clock; + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java index 4f03c166..dcc792a0 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.token; +import java.time.Clock; import java.time.Instant; import java.util.Base64; import java.util.Collections; @@ -52,6 +53,7 @@ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator accessTokenCustomizer; @@ -71,7 +73,7 @@ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator + * The provided clock must not be {@code null}. + * This allows injecting a custom clock for testing or + * adjusting time-related behavior. + * + * @param clock the {@link Clock} instance to set, must not be {@code null} + * @throws IllegalArgumentException if {@code clock} is {@code null} + */ + public void setClock(Clock clock) { + Assert.notNull(clock, "Clock must not be null"); + this.clock = clock; + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java index 8c5855aa..7bd2bfc6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.token; +import java.time.Clock; import java.time.Instant; import java.util.Base64; @@ -26,6 +27,7 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.OAuth2RefreshToken; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken; +import org.springframework.util.Assert; /** * An {@link OAuth2TokenGenerator} that generates an {@link OAuth2RefreshToken}. @@ -39,6 +41,7 @@ public final class OAuth2RefreshTokenGenerator implements OAuth2TokenGenerator + * The provided clock must not be {@code null}. + * This allows injecting a custom clock for testing or + * adjusting time-related behavior. + * + * @param clock the {@link Clock} instance to set, must not be {@code null} + * @throws IllegalArgumentException if {@code clock} is {@code null} + */ + public void setClock(Clock clock) { + Assert.notNull(clock, "Clock must not be null"); + this.clock = clock; + } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java index ed13047e..bbdd4baf 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java @@ -19,6 +19,7 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.Principal; +import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -133,6 +134,7 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { this.jwtEncoder = mock(JwtEncoder.class); this.jwtCustomizer = mock(OAuth2TokenCustomizer.class); JwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(this.jwtCustomizer); this.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java index 6ad95f50..29a6b494 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.authentication; +import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -105,6 +106,7 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { this.jwtEncoder = mock(JwtEncoder.class); this.jwtCustomizer = mock(OAuth2TokenCustomizer.class); JwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(this.jwtCustomizer); this.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java index 32eb57ed..e4d330c0 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java @@ -16,6 +16,7 @@ package org.springframework.security.oauth2.server.authorization.authentication; import java.security.Principal; +import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Collections; @@ -120,6 +121,7 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { given(this.jwtEncoder.encode(any())).willReturn(createJwt(Collections.singleton("scope1"))); this.jwtCustomizer = mock(OAuth2TokenCustomizer.class); JwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(this.jwtCustomizer); this.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java index 4d8b1d67..61aec491 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java @@ -21,6 +21,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.text.MessageFormat; +import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -1234,6 +1235,7 @@ public class OAuth2AuthorizationCodeGrantTests { @Bean OAuth2TokenGenerator tokenGenerator() { JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder()); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2TokenGenerator refreshTokenGenerator = new CustomRefreshTokenGenerator(); return new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator); @@ -1296,6 +1298,7 @@ public class OAuth2AuthorizationCodeGrantTests { @Bean OAuth2TokenGenerator tokenGenerator() { JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder()); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator( diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java index cd4de9ff..3b69e26e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java @@ -20,6 +20,7 @@ import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.Principal; +import java.time.Clock; import java.util.Base64; import java.util.HashSet; import java.util.List; @@ -720,6 +721,7 @@ public class OidcTests { @Bean OAuth2TokenGenerator tokenGenerator() { JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource())); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator( @@ -761,6 +763,7 @@ public class OidcTests { @Bean OAuth2TokenGenerator tokenGenerator() { JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource())); + jwtGenerator.setClock(Clock.systemUTC()); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2TokenGenerator refreshTokenGenerator = new CustomRefreshTokenGenerator(); return new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java index bb95c02e..1234ebf0 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java @@ -15,6 +15,7 @@ */ package org.springframework.security.oauth2.server.authorization.oidc.authentication; +import java.time.Clock; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -108,6 +109,7 @@ public class OidcClientRegistrationAuthenticationProviderTests { this.authorizationService = mock(OAuth2AuthorizationService.class); this.jwtEncoder = mock(JwtEncoder.class); JwtGenerator jwtGenerator = new JwtGenerator(this.jwtEncoder); + jwtGenerator.setClock(Clock.systemUTC()); this.tokenGenerator = spy(new OAuth2TokenGenerator() { @Override public Jwt generate(OAuth2TokenContext context) { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java index c457cf7d..ced0b4b2 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java @@ -16,6 +16,7 @@ package org.springframework.security.oauth2.server.authorization.token; import java.security.Principal; +import java.time.Clock; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Date; @@ -84,6 +85,7 @@ public class JwtGeneratorTests { this.jwtCustomizer = mock(OAuth2TokenCustomizer.class); this.jwtGenerator = new JwtGenerator(this.jwtEncoder); this.jwtGenerator.setJwtCustomizer(this.jwtCustomizer); + this.jwtGenerator.setClock(Clock.systemUTC()); AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() .issuer("https://provider.com") .build();