Skip to content

Commit

Permalink
Added ability to set token's lifespan for server mode
Browse files Browse the repository at this point in the history
Signed-off-by: Akvel <mtakvel@gmail.com>
  • Loading branch information
roshevec authored and ostrya committed Oct 6, 2023
1 parent e2cba43 commit e5ae925
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 7 deletions.
30 changes: 30 additions & 0 deletions mock/src/main/java/com/tngtech/keycloakmock/api/ServerConfig.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -25,6 +28,7 @@ public final class ServerConfig {
@Nonnull private final String defaultRealm;
@Nonnull private final List<String> resourcesToMapRolesTo;
@Nonnull private final Set<String> defaultScopes;
@Nonnull private final Duration tokenLifespan;

private ServerConfig(@Nonnull final Builder builder) {
this.port = builder.port;
Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -148,6 +153,16 @@ public Set<String> getDefaultScopes() {
return Collections.unmodifiableSet(defaultScopes);
}

/**
* Get access token lifespan
*
* @return token lifespan
*/
@Nonnull
public Duration getTokenLifespan() {
return tokenLifespan;
}

/**
* Builder for {@link ServerConfig}.
*
Expand All @@ -162,6 +177,7 @@ public static final class Builder {
@Nonnull private String defaultRealm = DEFAULT_REALM;
@Nonnull private final List<String> resourcesToMapRolesTo = new ArrayList<>();
@Nonnull private final Set<String> defaultScopes = new HashSet<>();
@Nonnull private Duration tokenLifespan = DEFAULT_TOKEN_LIFESPAN;

private Builder() {
defaultScopes.add(DEFAULT_SCOPE);
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,6 +97,13 @@ public Set<String> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,14 +28,18 @@ public class TokenHelper {
@Nonnull private final List<String> resourcesToMapRolesTo;
@Nonnull private final Set<String> defaultScopes;

@Nonnull private final Duration tokenLifespan;

@Inject
TokenHelper(
@Nonnull TokenGenerator tokenGenerator,
@Nonnull @Named("resources") List<String> resourcesToMapRolesTo,
@Nonnull @Named("scopes") Set<String> defaultScopes) {
@Nonnull TokenGenerator tokenGenerator,
@Nonnull @Named("resources") List<String> resourcesToMapRolesTo,
@Nonnull @Named("scopes") Set<String> defaultScopes,
@Nonnull @Named("tokenLifespan") Duration tokenLifespan) {
this.tokenGenerator = tokenGenerator;
this.resourcesToMapRolesTo = resourcesToMapRolesTo;
this.defaultScopes = defaultScopes;
this.tokenLifespan = tokenLifespan;
}

@Nullable
Expand All @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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());
Expand All @@ -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);

Expand Down

0 comments on commit e5ae925

Please sign in to comment.