mirror of https://github.com/halo-dev/halo
feat: Allow configuration for token and refresh token time-to-live (#1853)
parent
9dd778bdab
commit
eee58989fe
|
@ -26,10 +26,6 @@ public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
|
||||||
"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
"https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||||
private final OAuth2AuthorizationService authorizationService;
|
private final OAuth2AuthorizationService authorizationService;
|
||||||
/**
|
|
||||||
* TODO from token settings
|
|
||||||
*/
|
|
||||||
private static final boolean isReuseRefreshTokens = false;
|
|
||||||
|
|
||||||
public JwtDaoAuthenticationProvider(
|
public JwtDaoAuthenticationProvider(
|
||||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
|
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
|
||||||
|
@ -89,9 +85,12 @@ public class JwtDaoAuthenticationProvider extends DaoAuthenticationProvider {
|
||||||
authorizationBuilder.accessToken(accessToken);
|
authorizationBuilder.accessToken(accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProviderSettings providerSettings =
|
||||||
|
ProviderContextHolder.getProviderContext().providerSettings();
|
||||||
|
|
||||||
// ----- Refresh token -----
|
// ----- Refresh token -----
|
||||||
OAuth2RefreshToken currentRefreshToken = refreshToken.getToken();
|
OAuth2RefreshToken currentRefreshToken = refreshToken.getToken();
|
||||||
if (!isReuseRefreshTokens) {
|
if (!providerSettings.isReuseRefreshTokens()) {
|
||||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||||
if (generatedRefreshToken == null) {
|
if (generatedRefreshToken == null) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package run.halo.app.identity.authentication;
|
package run.halo.app.identity.authentication;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
import org.springframework.security.oauth2.core.OAuth2AccessToken;
|
||||||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
||||||
|
@ -21,11 +20,11 @@ import run.halo.app.infra.utils.HaloUtils;
|
||||||
* used for an {@link OAuth2AccessToken}.
|
* used for an {@link OAuth2AccessToken}.
|
||||||
*
|
*
|
||||||
* @author guqing
|
* @author guqing
|
||||||
* @date 2022-04-14
|
|
||||||
* @see OAuth2TokenGenerator
|
* @see OAuth2TokenGenerator
|
||||||
* @see Jwt
|
* @see Jwt
|
||||||
* @see JwtEncoder
|
* @see JwtEncoder
|
||||||
* @see OAuth2AccessToken
|
* @see OAuth2AccessToken
|
||||||
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public record JwtGenerator(JwtEncoder jwtEncoder) implements OAuth2TokenGenerator<Jwt> {
|
public record JwtGenerator(JwtEncoder jwtEncoder) implements OAuth2TokenGenerator<Jwt> {
|
||||||
/**
|
/**
|
||||||
|
@ -46,9 +45,16 @@ public record JwtGenerator(JwtEncoder jwtEncoder) implements OAuth2TokenGenerato
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Instant issuedAt = Instant.now();
|
Instant issuedAt = Instant.now();
|
||||||
// TODO Allow configuration for ID Token time-to-live
|
|
||||||
Instant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);
|
ProviderSettings providerSettings = context.getProviderContext().providerSettings();
|
||||||
;
|
|
||||||
|
Instant expiresAt;
|
||||||
|
if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
|
||||||
|
expiresAt = issuedAt.plus(providerSettings.getAccessTokenTimeToLive());
|
||||||
|
} else {
|
||||||
|
// refresh token
|
||||||
|
expiresAt = issuedAt.plus(providerSettings.getRefreshTokenTimeToLive());
|
||||||
|
}
|
||||||
|
|
||||||
String issuer = null;
|
String issuer = null;
|
||||||
if (context.getProviderContext() != null) {
|
if (context.getProviderContext() != null) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author guqing
|
* @author guqing
|
||||||
* @date 2022-04-14
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public interface OAuth2TokenContext extends Context {
|
public interface OAuth2TokenContext extends Context {
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package run.halo.app.identity.authentication;
|
package run.halo.app.identity.authentication;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import run.halo.app.infra.config.AbstractSettings;
|
import run.halo.app.infra.config.AbstractSettings;
|
||||||
|
@ -9,7 +10,7 @@ import run.halo.app.infra.config.ConfigurationSettingNames;
|
||||||
* A facility for provider configuration settings.
|
* A facility for provider configuration settings.
|
||||||
*
|
*
|
||||||
* @author guqing
|
* @author guqing
|
||||||
* @date 2022-04-14
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public final class ProviderSettings extends AbstractSettings {
|
public final class ProviderSettings extends AbstractSettings {
|
||||||
private ProviderSettings(Map<String, Object> settings) {
|
private ProviderSettings(Map<String, Object> settings) {
|
||||||
|
@ -53,6 +54,32 @@ public final class ProviderSettings extends AbstractSettings {
|
||||||
return getSetting(ConfigurationSettingNames.Provider.JWK_SET_ENDPOINT);
|
return getSetting(ConfigurationSettingNames.Provider.JWK_SET_ENDPOINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time-to-live for an access token. The default is 5 minutes.
|
||||||
|
*
|
||||||
|
* @return the time-to-live for an access token
|
||||||
|
*/
|
||||||
|
public Duration getAccessTokenTimeToLive() {
|
||||||
|
return getSetting(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if refresh tokens are reused when returning the access token response,
|
||||||
|
* or {@code false} if a new refresh token is issued. The default is {@code true}.
|
||||||
|
*/
|
||||||
|
public boolean isReuseRefreshTokens() {
|
||||||
|
return getSetting(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time-to-live for a refresh token. The default is 60 minutes.
|
||||||
|
*
|
||||||
|
* @return the time-to-live for a refresh token
|
||||||
|
*/
|
||||||
|
public Duration getRefreshTokenTimeToLive() {
|
||||||
|
return getSetting(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link Builder} with the default settings.
|
* Constructs a new {@link Builder} with the default settings.
|
||||||
*
|
*
|
||||||
|
@ -60,9 +87,12 @@ public final class ProviderSettings extends AbstractSettings {
|
||||||
*/
|
*/
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder()
|
return new Builder()
|
||||||
.authorizationEndpoint("/oauth2/authorize")
|
.authorizationEndpoint("/api/v1/oauth2/authorize")
|
||||||
.tokenEndpoint("/oauth2/token")
|
.tokenEndpoint("/api/v1/oauth2/token")
|
||||||
.jwkSetEndpoint("/oauth2/jwks");
|
.jwkSetEndpoint("/api/v1/oauth2/jwks")
|
||||||
|
.accessTokenTimeToLive(Duration.ofMinutes(5L))
|
||||||
|
.refreshTokenTimeToLive(Duration.ofMinutes(60))
|
||||||
|
.reuseRefreshTokens(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,6 +156,48 @@ public final class ProviderSettings extends AbstractSettings {
|
||||||
return setting(ConfigurationSettingNames.Provider.JWK_SET_ENDPOINT, jwkSetEndpoint);
|
return setting(ConfigurationSettingNames.Provider.JWK_SET_ENDPOINT, jwkSetEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the time-to-live for an access token. Must be greater than {@code Duration.ZERO}.
|
||||||
|
*
|
||||||
|
* @param accessTokenTimeToLive the time-to-live for an access token
|
||||||
|
* @return the {@link Builder} for further configuration
|
||||||
|
*/
|
||||||
|
public Builder accessTokenTimeToLive(Duration accessTokenTimeToLive) {
|
||||||
|
Assert.notNull(accessTokenTimeToLive, "accessTokenTimeToLive cannot be null");
|
||||||
|
Assert.isTrue(accessTokenTimeToLive.getSeconds() > 0,
|
||||||
|
"accessTokenTimeToLive must be greater than Duration.ZERO");
|
||||||
|
return setting(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE,
|
||||||
|
accessTokenTimeToLive);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to {@code true} if refresh tokens are reused when returning the access token
|
||||||
|
* response,
|
||||||
|
* or {@code false} if a new refresh token is issued.
|
||||||
|
*
|
||||||
|
* @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} to issue
|
||||||
|
* new refresh tokens
|
||||||
|
* @return the {@link Builder} for further configuration
|
||||||
|
*/
|
||||||
|
public Builder reuseRefreshTokens(boolean reuseRefreshTokens) {
|
||||||
|
return setting(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS,
|
||||||
|
reuseRefreshTokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the time-to-live for a refresh token. Must be greater than {@code Duration.ZERO}.
|
||||||
|
*
|
||||||
|
* @param refreshTokenTimeToLive the time-to-live for a refresh token
|
||||||
|
* @return the {@link Builder} for further configuration
|
||||||
|
*/
|
||||||
|
public Builder refreshTokenTimeToLive(Duration refreshTokenTimeToLive) {
|
||||||
|
Assert.notNull(refreshTokenTimeToLive, "refreshTokenTimeToLive cannot be null");
|
||||||
|
Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0,
|
||||||
|
"refreshTokenTimeToLive must be greater than Duration.ZERO");
|
||||||
|
return setting(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE,
|
||||||
|
refreshTokenTimeToLive);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the {@link ProviderSettings}.
|
* Builds the {@link ProviderSettings}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -12,7 +12,7 @@ import org.springframework.util.Assert;
|
||||||
* Base implementation for configuration settings.
|
* Base implementation for configuration settings.
|
||||||
*
|
*
|
||||||
* @author guqing
|
* @author guqing
|
||||||
* @date 2022-04-14
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractSettings implements Serializable {
|
public abstract class AbstractSettings implements Serializable {
|
||||||
private final Map<String, Object> settings;
|
private final Map<String, Object> settings;
|
||||||
|
|
|
@ -59,14 +59,6 @@ public class ConfigurationSettingNames {
|
||||||
public static final String ACCESS_TOKEN_TIME_TO_LIVE =
|
public static final String ACCESS_TOKEN_TIME_TO_LIVE =
|
||||||
TOKEN_SETTINGS_NAMESPACE.concat("access-token-time-to-live");
|
TOKEN_SETTINGS_NAMESPACE.concat("access-token-time-to-live");
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the {@link OAuth2TokenFormat token format} for an access token.
|
|
||||||
*
|
|
||||||
* @since 0.2.3
|
|
||||||
*/
|
|
||||||
public static final String ACCESS_TOKEN_FORMAT =
|
|
||||||
TOKEN_SETTINGS_NAMESPACE.concat("access-token-format");
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to {@code true} if refresh tokens are reused when returning the access token
|
* Set to {@code true} if refresh tokens are reused when returning the access token
|
||||||
* response,
|
* response,
|
||||||
|
|
|
@ -6,7 +6,6 @@ import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -100,7 +99,15 @@ public class JwtGeneratorTest {
|
||||||
tokenContext.getPrincipal().getName());
|
tokenContext.getPrincipal().getName());
|
||||||
|
|
||||||
Instant issuedAt = Instant.now();
|
Instant issuedAt = Instant.now();
|
||||||
Instant expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);
|
|
||||||
|
ProviderSettings providerSettings = tokenContext.getProviderContext().providerSettings();
|
||||||
|
Instant expiresAt;
|
||||||
|
if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenContext.getTokenType())) {
|
||||||
|
expiresAt = issuedAt.plus(providerSettings.getAccessTokenTimeToLive());
|
||||||
|
} else {
|
||||||
|
expiresAt = issuedAt.plus(providerSettings.getAccessTokenTimeToLive());
|
||||||
|
}
|
||||||
|
|
||||||
assertThat(jwtClaimsSet.getIssuedAt()).isBetween(issuedAt.minusSeconds(1),
|
assertThat(jwtClaimsSet.getIssuedAt()).isBetween(issuedAt.minusSeconds(1),
|
||||||
issuedAt.plusSeconds(1));
|
issuedAt.plusSeconds(1));
|
||||||
assertThat(jwtClaimsSet.getExpiresAt()).isBetween(expiresAt.minusSeconds(1),
|
assertThat(jwtClaimsSet.getExpiresAt()).isBetween(expiresAt.minusSeconds(1),
|
||||||
|
|
|
@ -3,6 +3,7 @@ package run.halo.app.authentication;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import run.halo.app.identity.authentication.ProviderSettings;
|
import run.halo.app.identity.authentication.ProviderSettings;
|
||||||
|
|
||||||
|
@ -19,9 +20,13 @@ public class ProviderSettingsTest {
|
||||||
ProviderSettings providerSettings = ProviderSettings.builder().build();
|
ProviderSettings providerSettings = ProviderSettings.builder().build();
|
||||||
|
|
||||||
assertThat(providerSettings.getIssuer()).isNull();
|
assertThat(providerSettings.getIssuer()).isNull();
|
||||||
assertThat(providerSettings.getAuthorizationEndpoint()).isEqualTo("/oauth2/authorize");
|
assertThat(providerSettings.getAuthorizationEndpoint()).isEqualTo(
|
||||||
assertThat(providerSettings.getTokenEndpoint()).isEqualTo("/oauth2/token");
|
"/api/v1/oauth2/authorize");
|
||||||
assertThat(providerSettings.getJwkSetEndpoint()).isEqualTo("/oauth2/jwks");
|
assertThat(providerSettings.getTokenEndpoint()).isEqualTo("/api/v1/oauth2/token");
|
||||||
|
assertThat(providerSettings.getJwkSetEndpoint()).isEqualTo("/api/v1/oauth2/jwks");
|
||||||
|
assertThat(providerSettings.isReuseRefreshTokens()).isEqualTo(false);
|
||||||
|
assertThat(providerSettings.getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
|
||||||
|
assertThat(providerSettings.getRefreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -30,18 +35,27 @@ public class ProviderSettingsTest {
|
||||||
String tokenEndpoint = "/oauth2/v1/token";
|
String tokenEndpoint = "/oauth2/v1/token";
|
||||||
String jwkSetEndpoint = "/oauth2/v1/jwks";
|
String jwkSetEndpoint = "/oauth2/v1/jwks";
|
||||||
String issuer = "https://example.com:9000";
|
String issuer = "https://example.com:9000";
|
||||||
|
boolean isReuseRefreshTokens = true;
|
||||||
|
Duration accessTokenTimeToLive = Duration.ofMinutes(30);
|
||||||
|
Duration refreshTokenTimeToLive = Duration.ofMinutes(120);
|
||||||
|
|
||||||
ProviderSettings providerSettings = ProviderSettings.builder()
|
ProviderSettings providerSettings = ProviderSettings.builder()
|
||||||
.issuer(issuer)
|
.issuer(issuer)
|
||||||
.authorizationEndpoint(authorizationEndpoint)
|
.authorizationEndpoint(authorizationEndpoint)
|
||||||
.tokenEndpoint(tokenEndpoint)
|
.tokenEndpoint(tokenEndpoint)
|
||||||
.jwkSetEndpoint(jwkSetEndpoint)
|
.jwkSetEndpoint(jwkSetEndpoint)
|
||||||
|
.reuseRefreshTokens(isReuseRefreshTokens)
|
||||||
|
.accessTokenTimeToLive(accessTokenTimeToLive)
|
||||||
|
.refreshTokenTimeToLive(refreshTokenTimeToLive)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThat(providerSettings.getIssuer()).isEqualTo(issuer);
|
assertThat(providerSettings.getIssuer()).isEqualTo(issuer);
|
||||||
assertThat(providerSettings.getAuthorizationEndpoint()).isEqualTo(authorizationEndpoint);
|
assertThat(providerSettings.getAuthorizationEndpoint()).isEqualTo(authorizationEndpoint);
|
||||||
assertThat(providerSettings.getTokenEndpoint()).isEqualTo(tokenEndpoint);
|
assertThat(providerSettings.getTokenEndpoint()).isEqualTo(tokenEndpoint);
|
||||||
assertThat(providerSettings.getJwkSetEndpoint()).isEqualTo(jwkSetEndpoint);
|
assertThat(providerSettings.getJwkSetEndpoint()).isEqualTo(jwkSetEndpoint);
|
||||||
|
assertThat(providerSettings.isReuseRefreshTokens()).isEqualTo(isReuseRefreshTokens);
|
||||||
|
assertThat(providerSettings.getAccessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive);
|
||||||
|
assertThat(providerSettings.getRefreshTokenTimeToLive()).isEqualTo(refreshTokenTimeToLive);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -51,7 +65,7 @@ public class ProviderSettingsTest {
|
||||||
.settings(settings -> settings.put("name2", "value2"))
|
.settings(settings -> settings.put("name2", "value2"))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
assertThat(providerSettings.getSettings()).hasSize(5);
|
assertThat(providerSettings.getSettings()).hasSize(8);
|
||||||
assertThat(providerSettings.<String>getSetting("name1")).isEqualTo("value1");
|
assertThat(providerSettings.<String>getSetting("name1")).isEqualTo("value1");
|
||||||
assertThat(providerSettings.<String>getSetting("name2")).isEqualTo("value2");
|
assertThat(providerSettings.<String>getSetting("name2")).isEqualTo("value2");
|
||||||
}
|
}
|
||||||
|
@ -83,4 +97,32 @@ public class ProviderSettingsTest {
|
||||||
.isThrownBy(() -> ProviderSettings.builder().jwkSetEndpoint(null))
|
.isThrownBy(() -> ProviderSettings.builder().jwkSetEndpoint(null))
|
||||||
.withMessage("value cannot be null");
|
.withMessage("value cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void refreshTokenTimeToLiveWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> ProviderSettings.builder().refreshTokenTimeToLive(null))
|
||||||
|
.withMessage("refreshTokenTimeToLive cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void refreshTokenTimeToLiveWhenLessThanZeroThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> ProviderSettings.builder().refreshTokenTimeToLive(Duration.ZERO))
|
||||||
|
.withMessage("refreshTokenTimeToLive must be greater than Duration.ZERO");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void accessTokenTimeToLiveToLiveWhenNullThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> ProviderSettings.builder().accessTokenTimeToLive(null))
|
||||||
|
.withMessage("accessTokenTimeToLive cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void accessTokenTimeToLiveToLiveWhenLessThanZeroThenThrowIllegalArgumentException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> ProviderSettings.builder().accessTokenTimeToLive(Duration.ZERO))
|
||||||
|
.withMessage("accessTokenTimeToLive must be greater than Duration.ZERO");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue