|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright 2002-2021 the original author or authors.
|
|
|
|
|
|
+ * Copyright 2002-2022 the original author or authors.
|
|
*
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* you may not use this file except in compliance with the License.
|
|
@@ -19,6 +19,7 @@ package org.springframework.security.oauth2.client;
|
|
import java.time.Clock;
|
|
import java.time.Clock;
|
|
import java.time.Duration;
|
|
import java.time.Duration;
|
|
import java.time.Instant;
|
|
import java.time.Instant;
|
|
|
|
+import java.util.function.Function;
|
|
|
|
|
|
import reactor.core.publisher.Mono;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
@@ -45,6 +46,8 @@ public final class JwtBearerReactiveOAuth2AuthorizedClientProvider implements Re
|
|
|
|
|
|
private ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = new WebClientReactiveJwtBearerTokenResponseClient();
|
|
private ReactiveOAuth2AccessTokenResponseClient<JwtBearerGrantRequest> accessTokenResponseClient = new WebClientReactiveJwtBearerTokenResponseClient();
|
|
|
|
|
|
|
|
+ private Function<OAuth2AuthorizationContext, Mono<Jwt>> jwtAssertionResolver = this::resolveJwtAssertion;
|
|
|
|
+
|
|
private Duration clockSkew = Duration.ofSeconds(60);
|
|
private Duration clockSkew = Duration.ofSeconds(60);
|
|
|
|
|
|
private Clock clock = Clock.systemUTC();
|
|
private Clock clock = Clock.systemUTC();
|
|
@@ -74,10 +77,7 @@ public final class JwtBearerReactiveOAuth2AuthorizedClientProvider implements Re
|
|
// need for re-authorization
|
|
// need for re-authorization
|
|
return Mono.empty();
|
|
return Mono.empty();
|
|
}
|
|
}
|
|
- if (!(context.getPrincipal().getPrincipal() instanceof Jwt)) {
|
|
|
|
- return Mono.empty();
|
|
|
|
- }
|
|
|
|
- Jwt jwt = (Jwt) context.getPrincipal().getPrincipal();
|
|
|
|
|
|
+
|
|
// As per spec, in section 4.1 Using Assertions as Authorization Grants
|
|
// As per spec, in section 4.1 Using Assertions as Authorization Grants
|
|
// https://tools.ietf.org/html/rfc7521#section-4.1
|
|
// https://tools.ietf.org/html/rfc7521#section-4.1
|
|
//
|
|
//
|
|
@@ -90,13 +90,26 @@ public final class JwtBearerReactiveOAuth2AuthorizedClientProvider implements Re
|
|
// issued with a reasonably short lifetime. Clients can refresh an
|
|
// issued with a reasonably short lifetime. Clients can refresh an
|
|
// expired access token by requesting a new one using the same
|
|
// expired access token by requesting a new one using the same
|
|
// assertion, if it is still valid, or with a new assertion.
|
|
// assertion, if it is still valid, or with a new assertion.
|
|
- return Mono.just(new JwtBearerGrantRequest(clientRegistration, jwt))
|
|
|
|
|
|
+
|
|
|
|
+ // @formatter:off
|
|
|
|
+ return this.jwtAssertionResolver.apply(context)
|
|
|
|
+ .map((jwt) -> new JwtBearerGrantRequest(clientRegistration, jwt))
|
|
.flatMap(this.accessTokenResponseClient::getTokenResponse)
|
|
.flatMap(this.accessTokenResponseClient::getTokenResponse)
|
|
.onErrorMap(OAuth2AuthorizationException.class,
|
|
.onErrorMap(OAuth2AuthorizationException.class,
|
|
(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(),
|
|
(ex) -> new ClientAuthorizationException(ex.getError(), clientRegistration.getRegistrationId(),
|
|
ex))
|
|
ex))
|
|
.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
|
.map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
|
|
tokenResponse.getAccessToken()));
|
|
tokenResponse.getAccessToken()));
|
|
|
|
+ // @formatter:on
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private Mono<Jwt> resolveJwtAssertion(OAuth2AuthorizationContext context) {
|
|
|
|
+ // @formatter:off
|
|
|
|
+ return Mono.just(context)
|
|
|
|
+ .map((ctx) -> ctx.getPrincipal().getPrincipal())
|
|
|
|
+ .filter((principal) -> principal instanceof Jwt)
|
|
|
|
+ .cast(Jwt.class);
|
|
|
|
+ // @formatter:on
|
|
}
|
|
}
|
|
|
|
|
|
private boolean hasTokenExpired(OAuth2Token token) {
|
|
private boolean hasTokenExpired(OAuth2Token token) {
|
|
@@ -115,6 +128,17 @@ public final class JwtBearerReactiveOAuth2AuthorizedClientProvider implements Re
|
|
this.accessTokenResponseClient = accessTokenResponseClient;
|
|
this.accessTokenResponseClient = accessTokenResponseClient;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Sets the resolver used for resolving the {@link Jwt} assertion.
|
|
|
|
+ * @param jwtAssertionResolver the resolver used for resolving the {@link Jwt}
|
|
|
|
+ * assertion
|
|
|
|
+ * @since 5.7
|
|
|
|
+ */
|
|
|
|
+ public void setJwtAssertionResolver(Function<OAuth2AuthorizationContext, Mono<Jwt>> jwtAssertionResolver) {
|
|
|
|
+ Assert.notNull(jwtAssertionResolver, "jwtAssertionResolver cannot be null");
|
|
|
|
+ this.jwtAssertionResolver = jwtAssertionResolver;
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* Sets the maximum acceptable clock skew, which is used when checking the
|
|
* Sets the maximum acceptable clock skew, which is used when checking the
|
|
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is
|
|
* {@link OAuth2AuthorizedClient#getAccessToken() access token} expiry. The default is
|