diff --git a/mock/src/main/java/com/tngtech/keycloakmock/api/ServerConfig.java b/mock/src/main/java/com/tngtech/keycloakmock/api/ServerConfig.java index 8b6561b..83ab7e6 100644 --- a/mock/src/main/java/com/tngtech/keycloakmock/api/ServerConfig.java +++ b/mock/src/main/java/com/tngtech/keycloakmock/api/ServerConfig.java @@ -1,6 +1,8 @@ package com.tngtech.keycloakmock.api; import com.tngtech.keycloakmock.impl.Protocol; + +import java.time.Duration; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; @@ -17,6 +19,7 @@ public final class ServerConfig { private static final int DEFAULT_PORT = 8000; private static final String DEFAULT_REALM = "master"; private static final String DEFAULT_SCOPE = "openid"; + private static final Duration DEFAULT_TOKEN_LIFESPAN = Duration.ofHours(10); private final int port; @Nonnull private final Protocol protocol; @@ -25,6 +28,7 @@ public final class ServerConfig { @Nonnull private final String defaultRealm; @Nonnull private final List resourcesToMapRolesTo; @Nonnull private final Set defaultScopes; + @Nonnull private final Duration tokenLifespan; private ServerConfig(@Nonnull final Builder builder) { this.port = builder.port; @@ -34,6 +38,7 @@ private ServerConfig(@Nonnull final Builder builder) { this.defaultRealm = builder.defaultRealm; this.resourcesToMapRolesTo = builder.resourcesToMapRolesTo; this.defaultScopes = builder.defaultScopes; + this.tokenLifespan = builder.tokenLifespan; } /** @@ -148,6 +153,16 @@ public Set getDefaultScopes() { return Collections.unmodifiableSet(defaultScopes); } + /** + * Get access token lifespan + * + * @return token lifespan + */ + @Nonnull + public Duration getTokenLifespan() { + return tokenLifespan; + } + /** * Builder for {@link ServerConfig}. * @@ -162,6 +177,7 @@ public static final class Builder { @Nonnull private String defaultRealm = DEFAULT_REALM; @Nonnull private final List resourcesToMapRolesTo = new ArrayList<>(); @Nonnull private final Set defaultScopes = new HashSet<>(); + @Nonnull private Duration tokenLifespan = DEFAULT_TOKEN_LIFESPAN; private Builder() { defaultScopes.add(DEFAULT_SCOPE); @@ -365,6 +381,20 @@ public Builder withDefaultScope(@Nonnull final String defaultScope) { return this; } + /** + * Set default access token lifespan ("exp" filed will be set as issuedAt + tokenLifespan) + * By default lifespan 10 hours. + * + * @param tokenLifespan as duration + * @return builder + * + */ + @Nonnull + public Builder withTokenLifespan(@Nonnull final Duration tokenLifespan) { + this.tokenLifespan = tokenLifespan; + return this; + } + /** * Build the server configuration. * diff --git a/mock/src/main/java/com/tngtech/keycloakmock/api/TokenConfig.java b/mock/src/main/java/com/tngtech/keycloakmock/api/TokenConfig.java index 1eebbd7..e8d2aef 100644 --- a/mock/src/main/java/com/tngtech/keycloakmock/api/TokenConfig.java +++ b/mock/src/main/java/com/tngtech/keycloakmock/api/TokenConfig.java @@ -5,6 +5,7 @@ import io.jsonwebtoken.Jwts; import java.net.URI; import java.net.URISyntaxException; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -762,6 +763,11 @@ public Builder withAuthenticationContextClassReference( public TokenConfig build() { return new TokenConfig(this); } + + public Builder witTokenLifespan(Duration tokenLifespan) { + this.expiration = issuedAt.plus(tokenLifespan); + return this; + } } /** diff --git a/mock/src/main/java/com/tngtech/keycloakmock/impl/dagger/ServerModule.java b/mock/src/main/java/com/tngtech/keycloakmock/impl/dagger/ServerModule.java index 70ed932..e3441ff 100644 --- a/mock/src/main/java/com/tngtech/keycloakmock/impl/dagger/ServerModule.java +++ b/mock/src/main/java/com/tngtech/keycloakmock/impl/dagger/ServerModule.java @@ -35,6 +35,7 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; +import java.time.Duration; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; @@ -96,6 +97,13 @@ public Set provideScopes(@Nonnull ServerConfig serverConfig) { return serverConfig.getDefaultScopes(); } + @Provides + @Singleton + @Named("tokenLifespan") + public Duration provideTokenLifespan(@Nonnull ServerConfig serverConfig) { + return serverConfig.getTokenLifespan(); + } + @Provides @Singleton Buffer keystoreBuffer(@Nonnull KeyStore keyStore) { diff --git a/mock/src/main/java/com/tngtech/keycloakmock/impl/helper/TokenHelper.java b/mock/src/main/java/com/tngtech/keycloakmock/impl/helper/TokenHelper.java index 74a60e7..6ea2a65 100644 --- a/mock/src/main/java/com/tngtech/keycloakmock/impl/helper/TokenHelper.java +++ b/mock/src/main/java/com/tngtech/keycloakmock/impl/helper/TokenHelper.java @@ -7,6 +7,8 @@ import com.tngtech.keycloakmock.impl.UrlConfiguration; import com.tngtech.keycloakmock.impl.session.Session; import com.tngtech.keycloakmock.impl.session.UserData; + +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,14 +28,18 @@ public class TokenHelper { @Nonnull private final List resourcesToMapRolesTo; @Nonnull private final Set defaultScopes; + @Nonnull private final Duration tokenLifespan; + @Inject TokenHelper( - @Nonnull TokenGenerator tokenGenerator, - @Nonnull @Named("resources") List resourcesToMapRolesTo, - @Nonnull @Named("scopes") Set defaultScopes) { + @Nonnull TokenGenerator tokenGenerator, + @Nonnull @Named("resources") List resourcesToMapRolesTo, + @Nonnull @Named("scopes") Set defaultScopes, + @Nonnull @Named("tokenLifespan") Duration tokenLifespan) { this.tokenGenerator = tokenGenerator; this.resourcesToMapRolesTo = resourcesToMapRolesTo; this.defaultScopes = defaultScopes; + this.tokenLifespan = tokenLifespan; } @Nullable @@ -53,7 +59,8 @@ public String getToken(@Nonnull Session session, @Nonnull UrlConfiguration reque .withClaim(SESSION_STATE, session.getSessionId()) // we currently don't do proper authorization anyway, so we can just act as if we were // compliant to ISO/IEC 29115 level 1 (see KEYCLOAK-3223 / KEYCLOAK-3314) - .withAuthenticationContextClassReference("1"); + .withAuthenticationContextClassReference("1") + .witTokenLifespan(tokenLifespan); if (session.getNonce() != null) { builder.withClaim(NONCE, session.getNonce()); } diff --git a/mock/src/test/java/com/tngtech/keycloakmock/impl/helper/TokenHelperTest.java b/mock/src/test/java/com/tngtech/keycloakmock/impl/helper/TokenHelperTest.java index dd50c36..95da198 100644 --- a/mock/src/test/java/com/tngtech/keycloakmock/impl/helper/TokenHelperTest.java +++ b/mock/src/test/java/com/tngtech/keycloakmock/impl/helper/TokenHelperTest.java @@ -10,6 +10,7 @@ import com.tngtech.keycloakmock.impl.UrlConfiguration; import com.tngtech.keycloakmock.impl.session.PersistentSession; import com.tngtech.keycloakmock.impl.session.UserData; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; @@ -55,7 +56,7 @@ void setup() { @Test void token_is_correctly_generated() { - uut = new TokenHelper(tokenGenerator, Collections.emptyList(), Collections.emptySet()); + uut = new TokenHelper(tokenGenerator, Collections.emptyList(), Collections.emptySet(), Duration.ofHours(100)); uut.getToken(session, urlConfiguration); @@ -68,7 +69,7 @@ void token_is_correctly_generated() { assertThat(tokenConfig.getClaims()) .containsEntry("nonce", NONCE) .containsEntry("session_state", SESSION_ID); - assertThat(tokenConfig.getExpiration()).isAfter(Instant.now().plus(9, ChronoUnit.HOURS)); + assertThat(tokenConfig.getExpiration()).isAfter(Instant.now().plus(99, ChronoUnit.HOURS)); assertThat(tokenConfig.getGivenName()).isEqualTo(USER.getGivenName()); assertThat(tokenConfig.getFamilyName()).isEqualTo(USER.getFamilyName()); assertThat(tokenConfig.getName()).isEqualTo(USER.getName()); @@ -82,7 +83,7 @@ void token_is_correctly_generated() { @Test void resource_roles_are_used_if_configured() { - uut = new TokenHelper(tokenGenerator, CONFIGURED_RESOURCES, Collections.emptySet()); + uut = new TokenHelper(tokenGenerator, CONFIGURED_RESOURCES, Collections.emptySet(), Duration.ofHours(10)); uut.getToken(session, urlConfiguration);