From 8f910ab0c4182668a4c2d82a10d8f61518a4f6cd Mon Sep 17 00:00:00 2001 From: Richard Treier Date: Thu, 21 Dec 2023 09:37:31 +0100 Subject: [PATCH] fix: issuedAt now validated with leeway in Oauth2ExpirationIssuedAtValidationRule --- .../oauth2/Oauth2ServiceConfiguration.java | 10 +++++ .../iam/oauth2/Oauth2ServiceExtension.java | 5 ++- ...auth2ExpirationIssuedAtValidationRule.java | 6 ++- .../Oauth2ValidationRulesRegistryImpl.java | 2 +- ...2ExpirationIssuedAtValidationRuleTest.java | 44 +++++++++++++++++-- 5 files changed, 60 insertions(+), 7 deletions(-) diff --git a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceConfiguration.java b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceConfiguration.java index 667a71afd67..04ecb710b45 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceConfiguration.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceConfiguration.java @@ -33,6 +33,7 @@ public class Oauth2ServiceConfiguration { private String publicCertificateAlias; private String providerAudience; private int notBeforeValidationLeeway; + private int issuedAtValidationLeeway; private String endpointAudience; private Long tokenExpiration; @@ -77,6 +78,10 @@ public int getNotBeforeValidationLeeway() { return notBeforeValidationLeeway; } + public int getIssuedAtValidationLeeway() { + return issuedAtValidationLeeway; + } + public String getEndpointAudience() { return endpointAudience; } @@ -146,6 +151,11 @@ public Builder notBeforeValidationLeeway(int notBeforeValidationLeeway) { return this; } + public Builder issuedAtValidationLeeway(int issuedAtValidationLeeway) { + configuration.issuedAtValidationLeeway = issuedAtValidationLeeway; + return this; + } + public Builder endpointAudience(String endpointAudience) { configuration.endpointAudience = endpointAudience; return this; diff --git a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java index 948e9f1db1b..1bbcf73b8a2 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/Oauth2ServiceExtension.java @@ -74,8 +74,10 @@ public class Oauth2ServiceExtension implements ServiceExtension { private static final String TOKEN_EXPIRATION = "edc.oauth.token.expiration"; // in minutes @Setting private static final String CLIENT_ID = "edc.oauth.client.id"; - @Setting + @Setting(value = "Leeway in seconds for validating the not before (nbf) claim in the token") private static final String NOT_BEFORE_LEEWAY = "edc.oauth.validation.nbf.leeway"; + @Setting(value = "Leeway in seconds for validating the issuedAt claim in the token") + private static final String ISSUED_AT_LEEWAY = "edc.oauth.validation.issued.at.leeway"; private IdentityProviderKeyResolver providerKeyResolver; @Inject @@ -167,6 +169,7 @@ private Oauth2ServiceConfiguration createConfig(ServiceExtensionContext context) .privateKeyResolver(privateKeyResolver) .certificateResolver(certificateResolver) .notBeforeValidationLeeway(context.getSetting(NOT_BEFORE_LEEWAY, 10)) + .issuedAtValidationLeeway(context.getSetting(ISSUED_AT_LEEWAY, 10)) .tokenExpiration(TimeUnit.MINUTES.toSeconds(tokenExpiration)) .build(); } diff --git a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/rule/Oauth2ExpirationIssuedAtValidationRule.java b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/rule/Oauth2ExpirationIssuedAtValidationRule.java index 8eb574f8495..863681408a9 100644 --- a/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/rule/Oauth2ExpirationIssuedAtValidationRule.java +++ b/extensions/common/iam/oauth2/oauth2-core/src/main/java/org/eclipse/edc/iam/oauth2/rule/Oauth2ExpirationIssuedAtValidationRule.java @@ -32,9 +32,11 @@ public class Oauth2ExpirationIssuedAtValidationRule implements TokenValidationRule { private final Clock clock; + private final int issuedAtValidationLeeway; - public Oauth2ExpirationIssuedAtValidationRule(Clock clock) { + public Oauth2ExpirationIssuedAtValidationRule(Clock clock, int issuedAtValidationLeeway) { this.clock = clock; + this.issuedAtValidationLeeway = issuedAtValidationLeeway; } @Override @@ -51,7 +53,7 @@ public Result checkRule(@NotNull ClaimToken toVerify, @Nullable Map + * Rounding of dates in JWT is within spec and the direction of rounding is platform-dependant. + */ + @Test + void validationOkWithRoundedIssuedAtAndMinimalLeeway() { + // time skew: tokens have dates rounded up to the second + var issuedAt = Instant.now().truncatedTo(ChronoUnit.SECONDS); + var expiresAt = issuedAt.plusSeconds(60); + + // time skew: the connector is still in the previous second, with unrounded dates + var now = issuedAt.minus(250, ChronoUnit.MILLIS); + + var clock = Clock.fixed(now, UTC); + var rule = new Oauth2ExpirationIssuedAtValidationRule(clock, 2); + + var token = ClaimToken.Builder.newInstance() + .claim(EXPIRATION_TIME, Date.from(expiresAt)) + .claim(ISSUED_AT, Date.from(issuedAt)) + .build(); + + var result = rule.checkRule(token, emptyMap()); + + assertThat(result.succeeded()).isTrue(); + } + @Test void validationKoBecauseExpirationTimeNotRespected() { var token = ClaimToken.Builder.newInstance() @@ -95,6 +135,4 @@ void validationKoBecauseIssuedAtInFuture() { assertThat(result.succeeded()).isFalse(); assertThat(result.getFailureMessages()).hasSize(1).contains("Current date/time before issued at (iat) claim in token"); } - - } \ No newline at end of file