Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ability to set token's lifespan for server mode #150

Merged
merged 2 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 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,7 @@
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 +18,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 +27,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 +37,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 +152,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 +176,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 +380,19 @@ 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,7 @@
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 +27,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 @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 +58,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,9 @@ 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 +71,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 +85,9 @@ 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
Loading