Skip to content

Commit bc38a4a

Browse files
committed
Provide configurable Clock in OAuth2AuthorizedClientProvider impls
Fixes gh-7114
1 parent 052256d commit bc38a4a

10 files changed

+152
-12
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProvider.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
2626
import org.springframework.util.Assert;
2727

28+
import java.time.Clock;
2829
import java.time.Duration;
2930
import java.time.Instant;
3031

@@ -41,6 +42,7 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
4142
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient =
4243
new DefaultClientCredentialsTokenResponseClient();
4344
private Duration clockSkew = Duration.ofSeconds(60);
45+
private Clock clock = Clock.systemUTC();
4446

4547
/**
4648
* Attempt to authorize (or re-authorize) the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -84,7 +86,7 @@ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
8486
}
8587

8688
private boolean hasTokenExpired(AbstractOAuth2Token token) {
87-
return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
89+
return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
8890
}
8991

9092
/**
@@ -100,7 +102,7 @@ public void setAccessTokenResponseClient(OAuth2AccessTokenResponseClient<OAuth2C
100102
/**
101103
* Sets the maximum acceptable clock skew, which is used when checking the
102104
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
103-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
105+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
104106
*
105107
* @param clockSkew the maximum acceptable clock skew
106108
*/
@@ -109,4 +111,14 @@ public void setClockSkew(Duration clockSkew) {
109111
Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
110112
this.clockSkew = clockSkew;
111113
}
114+
115+
/**
116+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
117+
*
118+
* @param clock the clock
119+
*/
120+
public void setClock(Clock clock) {
121+
Assert.notNull(clock, "clock cannot be null");
122+
this.clock = clock;
123+
}
112124
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProvider.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.util.Assert;
2525
import reactor.core.publisher.Mono;
2626

27+
import java.time.Clock;
2728
import java.time.Duration;
2829
import java.time.Instant;
2930

@@ -40,6 +41,7 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider imple
4041
private ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient =
4142
new WebClientReactiveClientCredentialsTokenResponseClient();
4243
private Duration clockSkew = Duration.ofSeconds(60);
44+
private Clock clock = Clock.systemUTC();
4345

4446
/**
4547
* Attempt to authorize (or re-authorize) the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -80,7 +82,7 @@ public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context
8082
}
8183

8284
private boolean hasTokenExpired(AbstractOAuth2Token token) {
83-
return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
85+
return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
8486
}
8587

8688
/**
@@ -96,7 +98,7 @@ public void setAccessTokenResponseClient(ReactiveOAuth2AccessTokenResponseClient
9698
/**
9799
* Sets the maximum acceptable clock skew, which is used when checking the
98100
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
99-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
101+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
100102
*
101103
* @param clockSkew the maximum acceptable clock skew
102104
*/
@@ -105,4 +107,14 @@ public void setClockSkew(Duration clockSkew) {
105107
Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
106108
this.clockSkew = clockSkew;
107109
}
110+
111+
/**
112+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
113+
*
114+
* @param clock the clock
115+
*/
116+
public void setClock(Clock clock) {
117+
Assert.notNull(clock, "clock cannot be null");
118+
this.clock = clock;
119+
}
108120
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
2121
import org.springframework.util.Assert;
2222

23+
import java.time.Clock;
2324
import java.time.Duration;
25+
import java.time.Instant;
2426
import java.util.LinkedHashMap;
2527
import java.util.List;
2628
import java.util.Map;
@@ -128,6 +130,7 @@ public OAuth2AuthorizedClientProviderBuilder refreshToken(Consumer<RefreshTokenG
128130
public class RefreshTokenGrantBuilder implements Builder {
129131
private OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;
130132
private Duration clockSkew;
133+
private Clock clock;
131134

132135
private RefreshTokenGrantBuilder() {
133136
}
@@ -145,7 +148,7 @@ public RefreshTokenGrantBuilder accessTokenResponseClient(OAuth2AccessTokenRespo
145148

146149
/**
147150
* Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
148-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
151+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
149152
*
150153
* @param clockSkew the maximum acceptable clock skew
151154
* @return the {@link RefreshTokenGrantBuilder}
@@ -155,6 +158,17 @@ public RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {
155158
return this;
156159
}
157160

161+
/**
162+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
163+
*
164+
* @param clock the clock
165+
* @return the {@link RefreshTokenGrantBuilder}
166+
*/
167+
public RefreshTokenGrantBuilder clock(Clock clock) {
168+
this.clock = clock;
169+
return this;
170+
}
171+
158172
/**
159173
* Builds an instance of {@link RefreshTokenOAuth2AuthorizedClientProvider}.
160174
*
@@ -169,6 +183,9 @@ public OAuth2AuthorizedClientProvider build() {
169183
if (this.clockSkew != null) {
170184
authorizedClientProvider.setClockSkew(this.clockSkew);
171185
}
186+
if (this.clock != null) {
187+
authorizedClientProvider.setClock(this.clock);
188+
}
172189
return authorizedClientProvider;
173190
}
174191
}
@@ -202,6 +219,7 @@ public OAuth2AuthorizedClientProviderBuilder clientCredentials(Consumer<ClientCr
202219
public class ClientCredentialsGrantBuilder implements Builder {
203220
private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
204221
private Duration clockSkew;
222+
private Clock clock;
205223

206224
private ClientCredentialsGrantBuilder() {
207225
}
@@ -219,7 +237,7 @@ public ClientCredentialsGrantBuilder accessTokenResponseClient(OAuth2AccessToken
219237

220238
/**
221239
* Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
222-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
240+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
223241
*
224242
* @param clockSkew the maximum acceptable clock skew
225243
* @return the {@link ClientCredentialsGrantBuilder}
@@ -229,6 +247,17 @@ public ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {
229247
return this;
230248
}
231249

250+
/**
251+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
252+
*
253+
* @param clock the clock
254+
* @return the {@link ClientCredentialsGrantBuilder}
255+
*/
256+
public ClientCredentialsGrantBuilder clock(Clock clock) {
257+
this.clock = clock;
258+
return this;
259+
}
260+
232261
/**
233262
* Builds an instance of {@link ClientCredentialsOAuth2AuthorizedClientProvider}.
234263
*
@@ -243,6 +272,9 @@ public OAuth2AuthorizedClientProvider build() {
243272
if (this.clockSkew != null) {
244273
authorizedClientProvider.setClockSkew(this.clockSkew);
245274
}
275+
if (this.clock != null) {
276+
authorizedClientProvider.setClock(this.clock);
277+
}
246278
return authorizedClientProvider;
247279
}
248280
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientProviderBuilder.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;
2121
import org.springframework.util.Assert;
2222

23+
import java.time.Clock;
2324
import java.time.Duration;
25+
import java.time.Instant;
2426
import java.util.LinkedHashMap;
2527
import java.util.List;
2628
import java.util.Map;
@@ -128,6 +130,7 @@ public ReactiveOAuth2AuthorizedClientProviderBuilder refreshToken(Consumer<Refre
128130
public class RefreshTokenGrantBuilder implements Builder {
129131
private ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;
130132
private Duration clockSkew;
133+
private Clock clock;
131134

132135
private RefreshTokenGrantBuilder() {
133136
}
@@ -145,7 +148,7 @@ public RefreshTokenGrantBuilder accessTokenResponseClient(ReactiveOAuth2AccessTo
145148

146149
/**
147150
* Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
148-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
151+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
149152
*
150153
* @param clockSkew the maximum acceptable clock skew
151154
* @return the {@link RefreshTokenGrantBuilder}
@@ -155,6 +158,17 @@ public RefreshTokenGrantBuilder clockSkew(Duration clockSkew) {
155158
return this;
156159
}
157160

161+
/**
162+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
163+
*
164+
* @param clock the clock
165+
* @return the {@link RefreshTokenGrantBuilder}
166+
*/
167+
public RefreshTokenGrantBuilder clock(Clock clock) {
168+
this.clock = clock;
169+
return this;
170+
}
171+
158172
/**
159173
* Builds an instance of {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider}.
160174
*
@@ -169,6 +183,9 @@ public ReactiveOAuth2AuthorizedClientProvider build() {
169183
if (this.clockSkew != null) {
170184
authorizedClientProvider.setClockSkew(this.clockSkew);
171185
}
186+
if (this.clock != null) {
187+
authorizedClientProvider.setClock(this.clock);
188+
}
172189
return authorizedClientProvider;
173190
}
174191
}
@@ -202,6 +219,7 @@ public ReactiveOAuth2AuthorizedClientProviderBuilder clientCredentials(Consumer<
202219
public class ClientCredentialsGrantBuilder implements Builder {
203220
private ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
204221
private Duration clockSkew;
222+
private Clock clock;
205223

206224
private ClientCredentialsGrantBuilder() {
207225
}
@@ -219,7 +237,7 @@ public ClientCredentialsGrantBuilder accessTokenResponseClient(ReactiveOAuth2Acc
219237

220238
/**
221239
* Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
222-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
240+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
223241
*
224242
* @param clockSkew the maximum acceptable clock skew
225243
* @return the {@link ClientCredentialsGrantBuilder}
@@ -229,6 +247,17 @@ public ClientCredentialsGrantBuilder clockSkew(Duration clockSkew) {
229247
return this;
230248
}
231249

250+
/**
251+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
252+
*
253+
* @param clock the clock
254+
* @return the {@link ClientCredentialsGrantBuilder}
255+
*/
256+
public ClientCredentialsGrantBuilder clock(Clock clock) {
257+
this.clock = clock;
258+
return this;
259+
}
260+
232261
/**
233262
* Builds an instance of {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider}.
234263
*
@@ -243,6 +272,9 @@ public ReactiveOAuth2AuthorizedClientProvider build() {
243272
if (this.clockSkew != null) {
244273
authorizedClientProvider.setClockSkew(this.clockSkew);
245274
}
275+
if (this.clock != null) {
276+
authorizedClientProvider.setClock(this.clock);
277+
}
246278
return authorizedClientProvider;
247279
}
248280
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProvider.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
2525
import org.springframework.util.Assert;
2626

27+
import java.time.Clock;
2728
import java.time.Duration;
2829
import java.time.Instant;
2930
import java.util.Arrays;
@@ -44,6 +45,7 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
4445
private OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient =
4546
new DefaultRefreshTokenTokenResponseClient();
4647
private Duration clockSkew = Duration.ofSeconds(60);
48+
private Clock clock = Clock.systemUTC();
4749

4850
/**
4951
* Attempt to re-authorize the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -92,7 +94,7 @@ public OAuth2AuthorizedClient authorize(OAuth2AuthorizationContext context) {
9294
}
9395

9496
private boolean hasTokenExpired(AbstractOAuth2Token token) {
95-
return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
97+
return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
9698
}
9799

98100
/**
@@ -108,7 +110,7 @@ public void setAccessTokenResponseClient(OAuth2AccessTokenResponseClient<OAuth2R
108110
/**
109111
* Sets the maximum acceptable clock skew, which is used when checking the
110112
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
111-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
113+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
112114
*
113115
* @param clockSkew the maximum acceptable clock skew
114116
*/
@@ -117,4 +119,14 @@ public void setClockSkew(Duration clockSkew) {
117119
Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
118120
this.clockSkew = clockSkew;
119121
}
122+
123+
/**
124+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
125+
*
126+
* @param clock the clock
127+
*/
128+
public void setClock(Clock clock) {
129+
Assert.notNull(clock, "clock cannot be null");
130+
this.clock = clock;
131+
}
120132
}

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProvider.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.springframework.util.Assert;
2525
import reactor.core.publisher.Mono;
2626

27+
import java.time.Clock;
2728
import java.time.Duration;
2829
import java.time.Instant;
2930
import java.util.Arrays;
@@ -44,6 +45,7 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider implements
4445
private ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient =
4546
new WebClientReactiveRefreshTokenTokenResponseClient();
4647
private Duration clockSkew = Duration.ofSeconds(60);
48+
private Clock clock = Clock.systemUTC();
4749

4850
/**
4951
* Attempt to re-authorize the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -91,7 +93,7 @@ public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context
9193
}
9294

9395
private boolean hasTokenExpired(AbstractOAuth2Token token) {
94-
return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
96+
return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
9597
}
9698

9799
/**
@@ -107,7 +109,7 @@ public void setAccessTokenResponseClient(ReactiveOAuth2AccessTokenResponseClient
107109
/**
108110
* Sets the maximum acceptable clock skew, which is used when checking the
109111
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
110-
* An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
112+
* An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
111113
*
112114
* @param clockSkew the maximum acceptable clock skew
113115
*/
@@ -116,4 +118,14 @@ public void setClockSkew(Duration clockSkew) {
116118
Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
117119
this.clockSkew = clockSkew;
118120
}
121+
122+
/**
123+
* Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
124+
*
125+
* @param clock the clock
126+
*/
127+
public void setClock(Clock clock) {
128+
Assert.notNull(clock, "clock cannot be null");
129+
this.clock = clock;
130+
}
119131
}

0 commit comments

Comments
 (0)