Skip to content

Commit 3417939

Browse files
committed
Allow configuring OIDC ID token ttl using TokenSettings
Closes spring-projectsgh-1689
1 parent f885df4 commit 3417939

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* The names for all the configuration settings.
2626
*
2727
* @author Joe Grandja
28+
* @author Shyngys Sapraliyev
2829
* @since 0.2.0
2930
*/
3031
public final class ConfigurationSettingNames {
@@ -223,6 +224,11 @@ public static final class Token {
223224
public static final String ID_TOKEN_SIGNATURE_ALGORITHM = TOKEN_SETTINGS_NAMESPACE
224225
.concat("id-token-signature-algorithm");
225226

227+
/**
228+
* Set the time-to-live for the {@link OidcIdToken ID Token}.
229+
*/
230+
public static final String ID_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE.concat("id-token-time-to-live");
231+
226232
/**
227233
* Set to {@code true} if access tokens must be bound to the client
228234
* {@code X509Certificate} received during client authentication when using the

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* A facility for token configuration settings.
2727
*
2828
* @author Joe Grandja
29+
* @author Shyngys Sapraliyev
2930
* @since 0.0.2
3031
* @see AbstractSettings
3132
* @see ConfigurationSettingNames.Token
@@ -102,6 +103,15 @@ public SignatureAlgorithm getIdTokenSignatureAlgorithm() {
102103
return getSetting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM);
103104
}
104105

106+
/**
107+
* Returns the time-to-live for the {@link OidcIdToken ID Token}. The default is 30
108+
* minutes.
109+
* @return the time-to-live for the {@link OidcIdToken ID Token}
110+
*/
111+
public Duration getIdTokenTimeToLive() {
112+
return getSetting(ConfigurationSettingNames.Token.ID_TOKEN_TIME_TO_LIVE);
113+
}
114+
105115
/**
106116
* Returns {@code true} if access tokens must be bound to the client
107117
* {@code X509Certificate} received during client authentication when using the
@@ -127,6 +137,7 @@ public static Builder builder() {
127137
.reuseRefreshTokens(true)
128138
.refreshTokenTimeToLive(Duration.ofMinutes(60))
129139
.idTokenSignatureAlgorithm(SignatureAlgorithm.RS256)
140+
.idTokenTimeToLive(Duration.ofMinutes(30))
130141
.x509CertificateBoundAccessTokens(false);
131142
}
132143

@@ -238,6 +249,18 @@ public Builder idTokenSignatureAlgorithm(SignatureAlgorithm idTokenSignatureAlgo
238249
return setting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM, idTokenSignatureAlgorithm);
239250
}
240251

252+
/**
253+
* Set the time-to-live for the {@link OidcIdToken ID Token}. Must be greater than
254+
* {@code Duration.ZERO}.
255+
* @param idTokenTimeToLive the time-to-live for the {@link OidcIdToken ID Token}
256+
* @return the {@link Builder} for further configuration
257+
*/
258+
public Builder idTokenTimeToLive(Duration idTokenTimeToLive) {
259+
Assert.notNull(idTokenTimeToLive, "idTokenTimeToLive cannot be null");
260+
Assert.isTrue(idTokenTimeToLive.getSeconds() > 0, "idTokenTimeToLive must be greater than Duration.ZERO");
261+
return setting(ConfigurationSettingNames.Token.ID_TOKEN_TIME_TO_LIVE, idTokenTimeToLive);
262+
}
263+
241264
/**
242265
* Set to {@code true} if access tokens must be bound to the client
243266
* {@code X509Certificate} received during client authentication when using the

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package org.springframework.security.oauth2.server.authorization.token;
1717

1818
import java.time.Instant;
19-
import java.time.temporal.ChronoUnit;
2019
import java.util.Collections;
2120
import java.util.Date;
2221
import java.util.UUID;
@@ -49,6 +48,7 @@
4948
* {@link OAuth2AccessToken} or {@link OidcIdToken}.
5049
*
5150
* @author Joe Grandja
51+
* @author Shyngys Sapraliyev
5252
* @since 0.2.3
5353
* @see OAuth2TokenGenerator
5454
* @see Jwt
@@ -97,9 +97,9 @@ public Jwt generate(OAuth2TokenContext context) {
9797
Instant issuedAt = Instant.now();
9898
Instant expiresAt;
9999
JwsAlgorithm jwsAlgorithm = SignatureAlgorithm.RS256;
100+
100101
if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {
101-
// TODO Allow configuration for ID Token time-to-live
102-
expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);
102+
expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getIdTokenTimeToLive());
103103
if (registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm() != null) {
104104
jwsAlgorithm = registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm();
105105
}

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,22 @@
2828
* Tests for {@link TokenSettings}.
2929
*
3030
* @author Joe Grandja
31+
* @author Shyngys Sapraliyev
3132
*/
3233
public class TokenSettingsTests {
3334

3435
@Test
3536
public void buildWhenDefaultThenDefaultsAreSet() {
3637
TokenSettings tokenSettings = TokenSettings.builder().build();
37-
assertThat(tokenSettings.getSettings()).hasSize(8);
38+
assertThat(tokenSettings.getSettings()).hasSize(9);
3839
assertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
3940
assertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(Duration.ofMinutes(5));
4041
assertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.SELF_CONTAINED);
4142
assertThat(tokenSettings.getDeviceCodeTimeToLive()).isEqualTo(Duration.ofMinutes(5));
4243
assertThat(tokenSettings.isReuseRefreshTokens()).isTrue();
4344
assertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(Duration.ofMinutes(60));
4445
assertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(SignatureAlgorithm.RS256);
46+
assertThat(tokenSettings.getIdTokenTimeToLive()).isEqualTo(Duration.ofMinutes(30));
4547
assertThat(tokenSettings.isX509CertificateBoundAccessTokens()).isFalse();
4648
}
4749

@@ -142,6 +144,31 @@ public void refreshTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgume
142144
.isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO");
143145
}
144146

147+
@Test
148+
public void idTokenTimeToLiveWhenProvidedThenSet() {
149+
Duration idTokenTimeToLive = Duration.ofMinutes(15);
150+
TokenSettings tokenSettings = TokenSettings.builder().idTokenTimeToLive(idTokenTimeToLive).build();
151+
assertThat(tokenSettings.getIdTokenTimeToLive()).isEqualTo(idTokenTimeToLive);
152+
}
153+
154+
@Test
155+
public void idTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() {
156+
assertThatThrownBy(() -> TokenSettings.builder().idTokenTimeToLive(null))
157+
.isInstanceOf(IllegalArgumentException.class)
158+
.extracting(Throwable::getMessage)
159+
.isEqualTo("idTokenTimeToLive cannot be null");
160+
161+
assertThatThrownBy(() -> TokenSettings.builder().idTokenTimeToLive(Duration.ZERO))
162+
.isInstanceOf(IllegalArgumentException.class)
163+
.extracting(Throwable::getMessage)
164+
.isEqualTo("idTokenTimeToLive must be greater than Duration.ZERO");
165+
166+
assertThatThrownBy(() -> TokenSettings.builder().idTokenTimeToLive(Duration.ofSeconds(-10)))
167+
.isInstanceOf(IllegalArgumentException.class)
168+
.extracting(Throwable::getMessage)
169+
.isEqualTo("idTokenTimeToLive must be greater than Duration.ZERO");
170+
}
171+
145172
@Test
146173
public void idTokenSignatureAlgorithmWhenProvidedThenSet() {
147174
SignatureAlgorithm idTokenSignatureAlgorithm = SignatureAlgorithm.RS512;
@@ -163,7 +190,7 @@ public void settingWhenCustomThenSet() {
163190
.setting("name1", "value1")
164191
.settings((settings) -> settings.put("name2", "value2"))
165192
.build();
166-
assertThat(tokenSettings.getSettings()).hasSize(10);
193+
assertThat(tokenSettings.getSettings()).hasSize(11);
167194
assertThat(tokenSettings.<String>getSetting("name1")).isEqualTo("value1");
168195
assertThat(tokenSettings.<String>getSetting("name2")).isEqualTo("value2");
169196
}

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
* Tests for {@link JwtGenerator}.
6666
*
6767
* @author Joe Grandja
68+
* @author Shyngys Sapraliyev
6869
*/
6970
public class JwtGeneratorTests {
7071

@@ -322,7 +323,7 @@ private void assertGeneratedTokenType(OAuth2TokenContext tokenContext) {
322323
expiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive());
323324
}
324325
else {
325-
expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES);
326+
expiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getIdTokenTimeToLive());
326327
}
327328
assertThat(jwtClaimsSet.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1));
328329
assertThat(jwtClaimsSet.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1));

0 commit comments

Comments
 (0)