From ed344ece7064144532cdea9a485cd5b5e562fa4d Mon Sep 17 00:00:00 2001 From: Josh Cummings <3627351+jzheaux@users.noreply.github.com> Date: Tue, 9 Sep 2025 16:22:07 -0600 Subject: [PATCH] Use Fixed Clock This commit stabilizes time-sensitive tests that verify the behavior of DPoP iat validation. Issue gh-14915 --- .../jwt/DPoPProofJwtDecoderFactoryTests.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactoryTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactoryTests.java index 1367bcb640..5c76181214 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactoryTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/DPoPProofJwtDecoderFactoryTests.java @@ -16,11 +16,14 @@ package org.springframework.security.oauth2.jwt; +import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; import java.util.Collections; import java.util.Map; import java.util.UUID; +import java.util.function.Function; import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.source.JWKSource; @@ -28,6 +31,7 @@ import com.nimbusds.jose.proc.SecurityContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jose.TestJwks; import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; @@ -343,13 +347,20 @@ public class DPoPProofJwtDecoderFactoryTests { String method = "GET"; String targetUri = "https://resource1"; + Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + JwtIssuedAtValidator issuedAtValidator = new JwtIssuedAtValidator(true); + issuedAtValidator.setClock(clock); + Function> validatorFactory = (context) -> issuedAtValidator; + DPoPProofJwtDecoderFactory jwtDecoderFactory = new DPoPProofJwtDecoderFactory(); + jwtDecoderFactory.setJwtValidatorFactory(validatorFactory); + // @formatter:off Map publicJwk = rsaJwk.toPublicJWK().toJSONObject(); JwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256) .type("dpop+jwt") .jwk(publicJwk) .build(); - Instant issuedAt = Instant.now().minus(Duration.ofSeconds(65)); // now minus 65 seconds + Instant issuedAt = Instant.now(clock).minus(Duration.ofSeconds(65)); // now minus 65 seconds JwtClaimsSet claims = JwtClaimsSet.builder() .issuedAt(issuedAt) .claim("htm", method) @@ -367,7 +378,7 @@ public class DPoPProofJwtDecoderFactoryTests { .build(); // @formatter:on - JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext); + JwtDecoder jwtDecoder = jwtDecoderFactory.createDecoder(dPoPProofContext); assertThatExceptionOfType(BadJwtException.class) .isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof())) @@ -382,13 +393,20 @@ public class DPoPProofJwtDecoderFactoryTests { String method = "GET"; String targetUri = "https://resource1"; + Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault()); + JwtIssuedAtValidator issuedAtValidator = new JwtIssuedAtValidator(true); + issuedAtValidator.setClock(clock); + Function> validatorFactory = (context) -> issuedAtValidator; + DPoPProofJwtDecoderFactory jwtDecoderFactory = new DPoPProofJwtDecoderFactory(); + jwtDecoderFactory.setJwtValidatorFactory(validatorFactory); + // @formatter:off Map publicJwk = rsaJwk.toPublicJWK().toJSONObject(); JwsHeader jwsHeader = JwsHeader.with(SignatureAlgorithm.RS256) .type("dpop+jwt") .jwk(publicJwk) .build(); - Instant issuedAt = Instant.now().plus(Duration.ofSeconds(65)); // now plus 65 seconds + Instant issuedAt = Instant.now(clock).plus(Duration.ofSeconds(65)); // now plus 65 seconds JwtClaimsSet claims = JwtClaimsSet.builder() .issuedAt(issuedAt) .claim("htm", method) @@ -406,7 +424,7 @@ public class DPoPProofJwtDecoderFactoryTests { .build(); // @formatter:on - JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(dPoPProofContext); + JwtDecoder jwtDecoder = jwtDecoderFactory.createDecoder(dPoPProofContext); assertThatExceptionOfType(BadJwtException.class) .isThrownBy(() -> jwtDecoder.decode(dPoPProofContext.getDPoPProof()))