Explorar el Código

Provide configurable Clock in OAuth2AuthorizedClientProvider impls

Fixes gh-7114
Joe Grandja hace 6 años
padre
commit
bc38a4a3cc
Se han modificado 10 ficheros con 152 adiciones y 12 borrados
  1. 14 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProvider.java
  2. 14 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProvider.java
  3. 34 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/OAuth2AuthorizedClientProviderBuilder.java
  4. 34 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/ReactiveOAuth2AuthorizedClientProviderBuilder.java
  5. 14 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProvider.java
  6. 14 2
      oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProvider.java
  7. 7 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProviderTests.java
  8. 7 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProviderTests.java
  9. 7 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProviderTests.java
  10. 7 0
      oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProviderTests.java

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

@@ -25,6 +25,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
 import org.springframework.util.Assert;
 
+import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
 
@@ -41,6 +42,7 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
 	private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient =
 			new DefaultClientCredentialsTokenResponseClient();
 	private Duration clockSkew = Duration.ofSeconds(60);
+	private Clock clock = Clock.systemUTC();
 
 	/**
 	 * Attempt to authorize (or re-authorize) the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -84,7 +86,7 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
 	}
 
 	private boolean hasTokenExpired(AbstractOAuth2Token token) {
-		return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
+		return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
 	}
 
 	/**
@@ -100,7 +102,7 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
 	/**
 	 * Sets the maximum acceptable clock skew, which is used when checking the
 	 * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
-	 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+	 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 	 *
 	 * @param clockSkew the maximum acceptable clock skew
 	 */
@@ -109,4 +111,14 @@ public final class ClientCredentialsOAuth2AuthorizedClientProvider implements OA
 		Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
 		this.clockSkew = clockSkew;
 	}
+
+	/**
+	 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+	 *
+	 * @param clock the clock
+	 */
+	public void setClock(Clock clock) {
+		Assert.notNull(clock, "clock cannot be null");
+		this.clock = clock;
+	}
 }

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

@@ -24,6 +24,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.util.Assert;
 import reactor.core.publisher.Mono;
 
+import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
 
@@ -40,6 +41,7 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider imple
 	private ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient =
 			new WebClientReactiveClientCredentialsTokenResponseClient();
 	private Duration clockSkew = Duration.ofSeconds(60);
+	private Clock clock = Clock.systemUTC();
 
 	/**
 	 * Attempt to authorize (or re-authorize) the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -80,7 +82,7 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider imple
 	}
 
 	private boolean hasTokenExpired(AbstractOAuth2Token token) {
-		return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
+		return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
 	}
 
 	/**
@@ -96,7 +98,7 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider imple
 	/**
 	 * Sets the maximum acceptable clock skew, which is used when checking the
 	 * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
-	 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+	 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 	 *
 	 * @param clockSkew the maximum acceptable clock skew
 	 */
@@ -105,4 +107,14 @@ public final class ClientCredentialsReactiveOAuth2AuthorizedClientProvider imple
 		Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
 		this.clockSkew = clockSkew;
 	}
+
+	/**
+	 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+	 *
+	 * @param clock the clock
+	 */
+	public void setClock(Clock clock) {
+		Assert.notNull(clock, "clock cannot be null");
+		this.clock = clock;
+	}
 }

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

@@ -20,7 +20,9 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentia
 import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
 import org.springframework.util.Assert;
 
+import java.time.Clock;
 import java.time.Duration;
+import java.time.Instant;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -128,6 +130,7 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 	public class RefreshTokenGrantBuilder implements Builder {
 		private OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;
 		private Duration clockSkew;
+		private Clock clock;
 
 		private RefreshTokenGrantBuilder() {
 		}
@@ -145,7 +148,7 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 
 		/**
 		 * Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
-		 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+		 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 		 *
 		 * @param clockSkew the maximum acceptable clock skew
 		 * @return the {@link RefreshTokenGrantBuilder}
@@ -155,6 +158,17 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+		 *
+		 * @param clock the clock
+		 * @return the {@link RefreshTokenGrantBuilder}
+		 */
+		public RefreshTokenGrantBuilder clock(Clock clock) {
+			this.clock = clock;
+			return this;
+		}
+
 		/**
 		 * Builds an instance of {@link RefreshTokenOAuth2AuthorizedClientProvider}.
 		 *
@@ -169,6 +183,9 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 			if (this.clockSkew != null) {
 				authorizedClientProvider.setClockSkew(this.clockSkew);
 			}
+			if (this.clock != null) {
+				authorizedClientProvider.setClock(this.clock);
+			}
 			return authorizedClientProvider;
 		}
 	}
@@ -202,6 +219,7 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 	public class ClientCredentialsGrantBuilder implements Builder {
 		private OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
 		private Duration clockSkew;
+		private Clock clock;
 
 		private ClientCredentialsGrantBuilder() {
 		}
@@ -219,7 +237,7 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 
 		/**
 		 * Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
-		 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+		 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 		 *
 		 * @param clockSkew the maximum acceptable clock skew
 		 * @return the {@link ClientCredentialsGrantBuilder}
@@ -229,6 +247,17 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+		 *
+		 * @param clock the clock
+		 * @return the {@link ClientCredentialsGrantBuilder}
+		 */
+		public ClientCredentialsGrantBuilder clock(Clock clock) {
+			this.clock = clock;
+			return this;
+		}
+
 		/**
 		 * Builds an instance of {@link ClientCredentialsOAuth2AuthorizedClientProvider}.
 		 *
@@ -243,6 +272,9 @@ public final class OAuth2AuthorizedClientProviderBuilder {
 			if (this.clockSkew != null) {
 				authorizedClientProvider.setClockSkew(this.clockSkew);
 			}
+			if (this.clock != null) {
+				authorizedClientProvider.setClock(this.clock);
+			}
 			return authorizedClientProvider;
 		}
 	}

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

@@ -20,7 +20,9 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGra
 import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient;
 import org.springframework.util.Assert;
 
+import java.time.Clock;
 import java.time.Duration;
+import java.time.Instant;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -128,6 +130,7 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 	public class RefreshTokenGrantBuilder implements Builder {
 		private ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient;
 		private Duration clockSkew;
+		private Clock clock;
 
 		private RefreshTokenGrantBuilder() {
 		}
@@ -145,7 +148,7 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 
 		/**
 		 * Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
-		 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+		 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 		 *
 		 * @param clockSkew the maximum acceptable clock skew
 		 * @return the {@link RefreshTokenGrantBuilder}
@@ -155,6 +158,17 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+		 *
+		 * @param clock the clock
+		 * @return the {@link RefreshTokenGrantBuilder}
+		 */
+		public RefreshTokenGrantBuilder clock(Clock clock) {
+			this.clock = clock;
+			return this;
+		}
+
 		/**
 		 * Builds an instance of {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider}.
 		 *
@@ -169,6 +183,9 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 			if (this.clockSkew != null) {
 				authorizedClientProvider.setClockSkew(this.clockSkew);
 			}
+			if (this.clock != null) {
+				authorizedClientProvider.setClock(this.clock);
+			}
 			return authorizedClientProvider;
 		}
 	}
@@ -202,6 +219,7 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 	public class ClientCredentialsGrantBuilder implements Builder {
 		private ReactiveOAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> accessTokenResponseClient;
 		private Duration clockSkew;
+		private Clock clock;
 
 		private ClientCredentialsGrantBuilder() {
 		}
@@ -219,7 +237,7 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 
 		/**
 		 * Sets the maximum acceptable clock skew, which is used when checking the access token expiry.
-		 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+		 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 		 *
 		 * @param clockSkew the maximum acceptable clock skew
 		 * @return the {@link ClientCredentialsGrantBuilder}
@@ -229,6 +247,17 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 			return this;
 		}
 
+		/**
+		 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+		 *
+		 * @param clock the clock
+		 * @return the {@link ClientCredentialsGrantBuilder}
+		 */
+		public ClientCredentialsGrantBuilder clock(Clock clock) {
+			this.clock = clock;
+			return this;
+		}
+
 		/**
 		 * Builds an instance of {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider}.
 		 *
@@ -243,6 +272,9 @@ public final class ReactiveOAuth2AuthorizedClientProviderBuilder {
 			if (this.clockSkew != null) {
 				authorizedClientProvider.setClockSkew(this.clockSkew);
 			}
+			if (this.clock != null) {
+				authorizedClientProvider.setClock(this.clock);
+			}
 			return authorizedClientProvider;
 		}
 	}

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

@@ -24,6 +24,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
 import org.springframework.util.Assert;
 
+import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Arrays;
@@ -44,6 +45,7 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
 	private OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient =
 			new DefaultRefreshTokenTokenResponseClient();
 	private Duration clockSkew = Duration.ofSeconds(60);
+	private Clock clock = Clock.systemUTC();
 
 	/**
 	 * Attempt to re-authorize the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -92,7 +94,7 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
 	}
 
 	private boolean hasTokenExpired(AbstractOAuth2Token token) {
-		return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
+		return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
 	}
 
 	/**
@@ -108,7 +110,7 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
 	/**
 	 * Sets the maximum acceptable clock skew, which is used when checking the
 	 * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
-	 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+	 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 	 *
 	 * @param clockSkew the maximum acceptable clock skew
 	 */
@@ -117,4 +119,14 @@ public final class RefreshTokenOAuth2AuthorizedClientProvider implements OAuth2A
 		Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
 		this.clockSkew = clockSkew;
 	}
+
+	/**
+	 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+	 *
+	 * @param clock the clock
+	 */
+	public void setClock(Clock clock) {
+		Assert.notNull(clock, "clock cannot be null");
+		this.clock = clock;
+	}
 }

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

@@ -24,6 +24,7 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
 import org.springframework.util.Assert;
 import reactor.core.publisher.Mono;
 
+import java.time.Clock;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.Arrays;
@@ -44,6 +45,7 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider implements
 	private ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> accessTokenResponseClient =
 			new WebClientReactiveRefreshTokenTokenResponseClient();
 	private Duration clockSkew = Duration.ofSeconds(60);
+	private Clock clock = Clock.systemUTC();
 
 	/**
 	 * Attempt to re-authorize the {@link OAuth2AuthorizationContext#getClientRegistration() client} in the provided {@code context}.
@@ -91,7 +93,7 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider implements
 	}
 
 	private boolean hasTokenExpired(AbstractOAuth2Token token) {
-		return token.getExpiresAt().isBefore(Instant.now().minus(this.clockSkew));
+		return token.getExpiresAt().isBefore(Instant.now(this.clock).minus(this.clockSkew));
 	}
 
 	/**
@@ -107,7 +109,7 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider implements
 	/**
 	 * Sets the maximum acceptable clock skew, which is used when checking the
 	 * {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is 60 seconds.
-	 * An access token is considered expired if it's before {@code Instant.now() - clockSkew}.
+	 * An access token is considered expired if it's before {@code Instant.now(this.clock) - clockSkew}.
 	 *
 	 * @param clockSkew the maximum acceptable clock skew
 	 */
@@ -116,4 +118,14 @@ public final class RefreshTokenReactiveOAuth2AuthorizedClientProvider implements
 		Assert.isTrue(clockSkew.getSeconds() >= 0, "clockSkew must be >= 0");
 		this.clockSkew = clockSkew;
 	}
+
+	/**
+	 * Sets the {@link Clock} used in {@link Instant#now(Clock)} when checking the access token expiry.
+	 *
+	 * @param clock the clock
+	 */
+	public void setClock(Clock clock) {
+		Assert.notNull(clock, "clock cannot be null");
+		this.clock = clock;
+	}
 }

+ 7 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsOAuth2AuthorizedClientProviderTests.java

@@ -78,6 +78,13 @@ public class ClientCredentialsOAuth2AuthorizedClientProviderTests {
 				.hasMessage("clockSkew must be >= 0");
 	}
 
+	@Test
+	public void setClockWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> this.authorizedClientProvider.setClock(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("clock cannot be null");
+	}
+
 	@Test
 	public void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {
 		assertThatThrownBy(() -> this.authorizedClientProvider.authorize(null))

+ 7 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/ClientCredentialsReactiveOAuth2AuthorizedClientProviderTests.java

@@ -79,6 +79,13 @@ public class ClientCredentialsReactiveOAuth2AuthorizedClientProviderTests {
 				.hasMessage("clockSkew must be >= 0");
 	}
 
+	@Test
+	public void setClockWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> this.authorizedClientProvider.setClock(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("clock cannot be null");
+	}
+
 	@Test
 	public void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {
 		assertThatThrownBy(() -> this.authorizedClientProvider.authorize(null).block())

+ 7 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenOAuth2AuthorizedClientProviderTests.java

@@ -87,6 +87,13 @@ public class RefreshTokenOAuth2AuthorizedClientProviderTests {
 				.hasMessage("clockSkew must be >= 0");
 	}
 
+	@Test
+	public void setClockWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> this.authorizedClientProvider.setClock(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("clock cannot be null");
+	}
+
 	@Test
 	public void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {
 		assertThatThrownBy(() -> this.authorizedClientProvider.authorize(null))

+ 7 - 0
oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/RefreshTokenReactiveOAuth2AuthorizedClientProviderTests.java

@@ -89,6 +89,13 @@ public class RefreshTokenReactiveOAuth2AuthorizedClientProviderTests {
 				.hasMessage("clockSkew must be >= 0");
 	}
 
+	@Test
+	public void setClockWhenNullThenThrowIllegalArgumentException() {
+		assertThatThrownBy(() -> this.authorizedClientProvider.setClock(null))
+				.isInstanceOf(IllegalArgumentException.class)
+				.hasMessage("clock cannot be null");
+	}
+
 	@Test
 	public void authorizeWhenContextIsNullThenThrowIllegalArgumentException() {
 		assertThatThrownBy(() -> this.authorizedClientProvider.authorize(null).block())