[java-oidc-common] branch main updated: JCOMOIDC-121 - Maximum lifetime for jti-claim validator's replay cache records
Henri Mikkonen
henri.mikkonen at iki.fi
Thu Sep 12 05:53:32 UTC 2024
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch main
in repository java-oidc-common.
View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=6df28bb04703d7e2d1a533588fcf8ce28d89c0d8
The following commit(s) were added to refs/heads/main by this push:
new 6df28bb JCOMOIDC-121 - Maximum lifetime for jti-claim validator's replay cache records
6df28bb is described below
commit 6df28bb04703d7e2d1a533588fcf8ce28d89c0d8
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Thu Sep 12 08:53:05 2024 +0300
JCOMOIDC-121 - Maximum lifetime for jti-claim validator's replay cache records
https://shibboleth.atlassian.net/browse/JCOMOIDC-121
If set (i.e. non-null), 'maxReplayCacheRecordLifetime' is used for overriding the expiration time
set by the 'exp' -claim, if the claim's expiry time is longer than it.
---
.../claims/impl/JWTIdentifierClaimsValidator.java | 28 ++++++++++++++++++++--
.../impl/JWTIdentifierClaimsValidatorTest.java | 24 +++++++++++++++++++
2 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
index e303bf2..0df4ec5 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
@@ -38,7 +38,7 @@ import net.shibboleth.shared.primitive.StringSupport;
* Verifies the identifier (jit) from the JWT against replay via configurable {@link ReplayCache}.
*
* <p>Single identifier is stored in the cache for the lifetime of the JWT (expiration instant is
- * taken from the 'exp' -claim).</p>
+ * taken from the 'exp' -claim if found, with configurable default and maximum lifetime).</p>
*/
public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
@@ -55,6 +55,11 @@ public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
*/
@Nullable private Duration replayCacheRecordLifetime;
+ /**
+ * Maximum lifetime to be used in replay cache when 'exp' claim is exploited.
+ */
+ @Nullable private Duration maxReplayCacheRecordLifetime;
+
/**
* Optional function to calculate they key used with {@link ReplayCache}.
*/
@@ -68,6 +73,7 @@ public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
assert oneMinute != null;
clockSkew = oneMinute;
replayCacheRecordLifetime = null;
+ maxReplayCacheRecordLifetime = null;
replayCacheKeyCalculationStrategy = null;
}
@@ -103,6 +109,18 @@ public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
replayCacheRecordLifetime = lifetime;
}
+ /**
+ * Set the maximum lifetime to be used in replay cache when 'exp' claim is exploited.
+ *
+ * @param lifetime record lifetime to set.
+ *
+ * @since 3.2.0
+ */
+ public void setMaxReplayCacheRecordLifetime(@Nullable final Duration lifetime) {
+ ifInitializedThrowUnmodifiabledComponentException();
+ maxReplayCacheRecordLifetime = lifetime;
+ }
+
/**
* Set the function to calculate they key used with {@link ReplayCache}.
*
@@ -138,7 +156,13 @@ public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
}
expiry = Instant.now().plus(replayCacheRecordLifetime);
} else {
- expiry = exp.toInstant().plus(clockSkew);
+ final Instant claimExpiry = exp.toInstant().plus(clockSkew);
+ if (maxReplayCacheRecordLifetime == null) {
+ expiry = claimExpiry;
+ } else {
+ final Instant maxExpiry = Instant.now().plus(maxReplayCacheRecordLifetime).plus(clockSkew);
+ expiry = maxExpiry.isBefore(claimExpiry) ? maxExpiry : claimExpiry;
+ }
}
final String jti = claims.getJWTID();
if (StringSupport.trimOrNull(jti) == null) {
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java
index 8e3f60f..078763a 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidatorTest.java
@@ -14,6 +14,7 @@
package net.shibboleth.oidc.security.jwt.claims.impl;
+import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.function.Function;
@@ -50,6 +51,11 @@ public class JWTIdentifierClaimsValidatorTest {
public void setup(@Nullable final Function<String, String> keyCalculationStrategy)
throws ComponentInitializationException {
+ setup(keyCalculationStrategy, null);
+ }
+
+ public void setup(@Nullable final Function<String, String> keyCalculationStrategy,
+ @Nullable final Duration maxLifetime) throws ComponentInitializationException {
validator = new JWTIdentifierClaimsValidator();
replayCache = new StorageServiceReplayCache();
final MemoryStorageService storageService = new MemoryStorageService();
@@ -59,6 +65,8 @@ public class JWTIdentifierClaimsValidatorTest {
validator.setReplayCache(replayCache);
validator.setId("test-validator");
validator.setReplayCacheKeyCalculationStrategy(keyCalculationStrategy);
+ validator.setClockSkew(Duration.ZERO);
+ validator.setMaxReplayCacheRecordLifetime(maxLifetime);
validator.initialize();
prc = new ProfileRequestContext();
}
@@ -193,4 +201,20 @@ public class JWTIdentifierClaimsValidatorTest {
validator.doValidate(claimsSet, prc);
}
+ @Test
+ public void doValidateTest_longExp()
+ throws JWTValidationException, ComponentInitializationException, InterruptedException {
+ setup(null, Duration.ofSeconds(2));
+ final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
+ .jwtID("mockId")
+ .expirationTime(Date.from(Instant.now().plus(Duration.ofHours(24))))
+ .build();
+ validator.doValidate(claimsSet, prc);
+ Assert.assertFalse(replayCache.check(JWTIdentifierClaimsValidator.class.getName(),
+ "mockId", Instant.now().plusSeconds(300)));
+ Thread.sleep(2000);
+ Assert.assertTrue(replayCache.check(JWTIdentifierClaimsValidator.class.getName(),
+ "mockId", Instant.now().plusSeconds(300)));
+ }
+
}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list