diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java index 747137db80..2490c0e653 100644 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java @@ -54,6 +54,10 @@ public class JWTClientCredentialsProvider implements ClientCredentialsProvider { this.tokenTimeout = tokenTimeout; } + protected int getTokenTimeout() { + return tokenTimeout; + } + @Override public void init(KeycloakDeployment deployment, Object config) { if (config == null || !(config instanceof Map)) { diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java index f97dce07f0..2ee2fcb37d 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java @@ -32,6 +32,7 @@ import javax.ws.rs.core.Response; import org.keycloak.OAuth2Constants; import org.keycloak.authentication.AuthenticationFlowError; import org.keycloak.authentication.ClientAuthenticationFlowContext; +import org.keycloak.common.util.Time; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.models.AuthenticationExecutionModel; @@ -145,6 +146,11 @@ public class JWTClientAuthenticator extends AbstractClientAuthenticator { throw new RuntimeException("Token is not active"); } + // KEYCLOAK-2986 + if (token.getExpiration() == 0 && token.getIssuedAt() + 10 < Time.currentTime()) { + throw new RuntimeException("Token is not active"); + } + context.success(); } catch (Exception e) { logger.errorValidatingAssertion(e); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java index 8a890df464..087a1e651b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/ClientAuthSignedJWTTest.java @@ -613,7 +613,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest { } @Test - @Ignore // Waiting for KEYCLOAK-2986 to be implemented + // KEYCLOAK-2986 public void testMissingExpirationClaim() throws Exception { // Missing only exp; the lifespan should be calculated from issuedAt OAuthClient.AccessTokenResponse response = testMissingClaim("expiration"); @@ -840,9 +840,7 @@ public class ClientAuthSignedJWTTest extends AbstractKeycloakTest { int now = Time.currentTime(); if (isClaimEnabled("issuedAt")) reqToken.issuedAt(now); - // For the time being there's no getter for tokenTimeout in JWTClientCredentialsProvider - // This is fine because KC doesn't care when exp claim is missing (see KEYCLOAK-2986) - /*if (isClaimEnabled("expiration")) reqToken.expiration(now + getTokenTimeout());*/ + if (isClaimEnabled("expiration")) reqToken.expiration(now + getTokenTimeout()); if (isClaimEnabled("notBefore")) reqToken.notBefore(now); return reqToken;