|
@@ -15,16 +15,6 @@
|
|
*/
|
|
*/
|
|
package org.springframework.security.oauth2.client.web.server;
|
|
package org.springframework.security.oauth2.client.web.server;
|
|
|
|
|
|
-import java.net.URI;
|
|
|
|
-import java.util.Base64;
|
|
|
|
-import java.util.HashMap;
|
|
|
|
-import java.util.Map;
|
|
|
|
-
|
|
|
|
-import org.springframework.http.HttpStatus;
|
|
|
|
-import org.springframework.http.server.reactive.ServerHttpRequest;
|
|
|
|
-import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
|
|
|
-import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
|
|
|
|
-import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
|
|
|
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
|
import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException;
|
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
|
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
|
@@ -32,19 +22,17 @@ import org.springframework.security.oauth2.client.registration.ReactiveClientReg
|
|
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
|
import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository;
|
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
|
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
|
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
|
|
-import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
|
|
|
|
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
|
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
|
|
import org.springframework.security.web.server.ServerRedirectStrategy;
|
|
import org.springframework.security.web.server.ServerRedirectStrategy;
|
|
-import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
|
|
|
|
-import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
|
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.util.Assert;
|
|
import org.springframework.web.server.ServerWebExchange;
|
|
import org.springframework.web.server.ServerWebExchange;
|
|
import org.springframework.web.server.WebFilter;
|
|
import org.springframework.web.server.WebFilter;
|
|
import org.springframework.web.server.WebFilterChain;
|
|
import org.springframework.web.server.WebFilterChain;
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
import org.springframework.web.util.UriComponentsBuilder;
|
|
-
|
|
|
|
import reactor.core.publisher.Mono;
|
|
import reactor.core.publisher.Mono;
|
|
|
|
|
|
|
|
+import java.net.URI;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* This {@code WebFilter} initiates the authorization code grant or implicit grant flow
|
|
* This {@code WebFilter} initiates the authorization code grant or implicit grant flow
|
|
* by redirecting the End-User's user-agent to the Authorization Server's Authorization Endpoint.
|
|
* by redirecting the End-User's user-agent to the Authorization Server's Authorization Endpoint.
|
|
@@ -63,10 +51,6 @@ import reactor.core.publisher.Mono;
|
|
* {@link ClientRegistration#getRegistrationId() registration identifier} of the client
|
|
* {@link ClientRegistration#getRegistrationId() registration identifier} of the client
|
|
* that is used for initiating the OAuth 2.0 Authorization Request.
|
|
* that is used for initiating the OAuth 2.0 Authorization Request.
|
|
*
|
|
*
|
|
- * <p>
|
|
|
|
- * <b>NOTE:</b> The default base {@code URI} {@code /oauth2/authorization} may be overridden
|
|
|
|
- * via it's constructor {@link #OAuth2AuthorizationRequestRedirectWebFilter(ReactiveClientRegistrationRepository, String)}.
|
|
|
|
-
|
|
|
|
* @author Rob Winch
|
|
* @author Rob Winch
|
|
* @since 5.1
|
|
* @since 5.1
|
|
* @see OAuth2AuthorizationRequest
|
|
* @see OAuth2AuthorizationRequest
|
|
@@ -79,17 +63,8 @@ import reactor.core.publisher.Mono;
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.2.1">Section 4.2.1 Authorization Request (Implicit)</a>
|
|
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.2.1">Section 4.2.1 Authorization Request (Implicit)</a>
|
|
*/
|
|
*/
|
|
public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|
public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|
- /**
|
|
|
|
- * The default base {@code URI} used for authorization requests.
|
|
|
|
- */
|
|
|
|
- public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
|
|
|
|
- private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
|
|
|
|
- private static final String AUTHORIZATION_REQUIRED_EXCEPTION_ATTR_NAME =
|
|
|
|
- ClientAuthorizationRequiredException.class.getName() + ".AUTHORIZATION_REQUIRED_EXCEPTION";
|
|
|
|
- private final ServerWebExchangeMatcher authorizationRequestMatcher;
|
|
|
|
- private final ReactiveClientRegistrationRepository clientRegistrationRepository;
|
|
|
|
private final ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
|
private final ServerRedirectStrategy authorizationRedirectStrategy = new DefaultServerRedirectStrategy();
|
|
- private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
|
|
|
|
|
|
+ private final ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver;
|
|
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
|
|
private ServerAuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository =
|
|
new WebSessionOAuth2ServerAuthorizationRequestRepository();
|
|
new WebSessionOAuth2ServerAuthorizationRequestRepository();
|
|
|
|
|
|
@@ -99,23 +74,17 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|
* @param clientRegistrationRepository the repository of client registrations
|
|
* @param clientRegistrationRepository the repository of client registrations
|
|
*/
|
|
*/
|
|
public OAuth2AuthorizationRequestRedirectWebFilter(ReactiveClientRegistrationRepository clientRegistrationRepository) {
|
|
public OAuth2AuthorizationRequestRedirectWebFilter(ReactiveClientRegistrationRepository clientRegistrationRepository) {
|
|
- this(clientRegistrationRepository, DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
|
|
|
|
|
|
+ this.authorizationRequestResolver = new DefaultServerOAuth2AuthorizationRequestResolver(clientRegistrationRepository);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
* Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided parameters.
|
|
* Constructs an {@code OAuth2AuthorizationRequestRedirectFilter} using the provided parameters.
|
|
*
|
|
*
|
|
- * @param clientRegistrationRepository the repository of client registrations
|
|
|
|
- * @param authorizationRequestBaseUri the base {@code URI} used for authorization requests
|
|
|
|
|
|
+ * @param authorizationRequestResolver the resolver to use
|
|
*/
|
|
*/
|
|
- public OAuth2AuthorizationRequestRedirectWebFilter(
|
|
|
|
- ReactiveClientRegistrationRepository clientRegistrationRepository, String authorizationRequestBaseUri) {
|
|
|
|
-
|
|
|
|
- Assert.hasText(authorizationRequestBaseUri, "authorizationRequestBaseUri cannot be empty");
|
|
|
|
- Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
|
|
|
|
- this.authorizationRequestMatcher = new PathPatternParserServerWebExchangeMatcher(
|
|
|
|
- authorizationRequestBaseUri + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
|
|
|
|
- this.clientRegistrationRepository = clientRegistrationRepository;
|
|
|
|
|
|
+ public OAuth2AuthorizationRequestRedirectWebFilter(ServerOAuth2AuthorizationRequestResolver authorizationRequestResolver) {
|
|
|
|
+ Assert.notNull(authorizationRequestResolver, "authorizationRequestResolver cannot be null");
|
|
|
|
+ this.authorizationRequestResolver = authorizationRequestResolver;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -131,54 +100,15 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
|
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
|
- return this.authorizationRequestMatcher.matches(exchange)
|
|
|
|
- .filter(matchResult -> matchResult.isMatch())
|
|
|
|
|
|
+ return this.authorizationRequestResolver.resolve(exchange)
|
|
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
|
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
|
- .map(ServerWebExchangeMatcher.MatchResult::getVariables)
|
|
|
|
- .map(variables -> variables.get(REGISTRATION_ID_URI_VARIABLE_NAME))
|
|
|
|
- .cast(String.class)
|
|
|
|
- .onErrorResume(ClientAuthorizationRequiredException.class, e -> Mono.just(e.getClientRegistrationId()))
|
|
|
|
- .flatMap(clientRegistrationId -> this.findByRegistrationId(exchange, clientRegistrationId))
|
|
|
|
|
|
+ .onErrorResume(ClientAuthorizationRequiredException.class, e -> this.authorizationRequestResolver.resolve(exchange, e.getClientRegistrationId()))
|
|
.flatMap(clientRegistration -> sendRedirectForAuthorization(exchange, clientRegistration));
|
|
.flatMap(clientRegistration -> sendRedirectForAuthorization(exchange, clientRegistration));
|
|
}
|
|
}
|
|
|
|
|
|
- private Mono<ClientRegistration> findByRegistrationId(ServerWebExchange exchange, String clientRegistration) {
|
|
|
|
- return this.clientRegistrationRepository.findByRegistrationId(clientRegistration)
|
|
|
|
- .switchIfEmpty(Mono.defer(() -> {
|
|
|
|
- exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
|
|
|
|
- return exchange.getResponse().setComplete().then(Mono.empty());
|
|
|
|
- }));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
private Mono<Void> sendRedirectForAuthorization(ServerWebExchange exchange,
|
|
private Mono<Void> sendRedirectForAuthorization(ServerWebExchange exchange,
|
|
- ClientRegistration clientRegistration) {
|
|
|
|
|
|
+ OAuth2AuthorizationRequest authorizationRequest) {
|
|
return Mono.defer(() -> {
|
|
return Mono.defer(() -> {
|
|
- String redirectUriStr = this
|
|
|
|
- .expandRedirectUri(exchange.getRequest(), clientRegistration);
|
|
|
|
-
|
|
|
|
- Map<String, Object> additionalParameters = new HashMap<>();
|
|
|
|
- additionalParameters.put(OAuth2ParameterNames.REGISTRATION_ID,
|
|
|
|
- clientRegistration.getRegistrationId());
|
|
|
|
-
|
|
|
|
- OAuth2AuthorizationRequest.Builder builder;
|
|
|
|
- if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
|
|
|
- builder = OAuth2AuthorizationRequest.authorizationCode();
|
|
|
|
- }
|
|
|
|
- else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
|
|
|
|
- builder = OAuth2AuthorizationRequest.implicit();
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- throw new IllegalArgumentException(
|
|
|
|
- "Invalid Authorization Grant Type (" + clientRegistration.getAuthorizationGrantType().getValue()
|
|
|
|
- + ") for Client Registration with Id: " + clientRegistration.getRegistrationId());
|
|
|
|
- }
|
|
|
|
- OAuth2AuthorizationRequest authorizationRequest = builder
|
|
|
|
- .clientId(clientRegistration.getClientId())
|
|
|
|
- .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
|
|
|
|
- .redirectUri(redirectUriStr).scopes(clientRegistration.getScopes())
|
|
|
|
- .state(this.stateGenerator.generateKey())
|
|
|
|
- .additionalParameters(additionalParameters).build();
|
|
|
|
-
|
|
|
|
Mono<Void> saveAuthorizationRequest = Mono.empty();
|
|
Mono<Void> saveAuthorizationRequest = Mono.empty();
|
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
|
|
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationRequest.getGrantType())) {
|
|
saveAuthorizationRequest = this.authorizationRequestRepository
|
|
saveAuthorizationRequest = this.authorizationRequestRepository
|
|
@@ -192,27 +122,4 @@ public class OAuth2AuthorizationRequestRedirectWebFilter implements WebFilter {
|
|
.then(this.authorizationRedirectStrategy.sendRedirect(exchange, redirectUri));
|
|
.then(this.authorizationRedirectStrategy.sendRedirect(exchange, redirectUri));
|
|
});
|
|
});
|
|
}
|
|
}
|
|
-
|
|
|
|
- private String expandRedirectUri(ServerHttpRequest request, ClientRegistration clientRegistration) {
|
|
|
|
- // Supported URI variables -> baseUrl, action, registrationId
|
|
|
|
- // Used in -> CommonOAuth2Provider.DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}"
|
|
|
|
- Map<String, String> uriVariables = new HashMap<>();
|
|
|
|
- uriVariables.put("registrationId", clientRegistration.getRegistrationId());
|
|
|
|
-
|
|
|
|
- String baseUrl = UriComponentsBuilder.fromHttpRequest(new ServerHttpRequestDecorator(request))
|
|
|
|
- .replacePath(request.getPath().contextPath().value())
|
|
|
|
- .replaceQuery(null)
|
|
|
|
- .build()
|
|
|
|
- .toUriString();
|
|
|
|
- uriVariables.put("baseUrl", baseUrl);
|
|
|
|
-
|
|
|
|
- if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
|
|
|
|
- String loginAction = "login";
|
|
|
|
- uriVariables.put("action", loginAction);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUriTemplate())
|
|
|
|
- .buildAndExpand(uriVariables)
|
|
|
|
- .toUriString();
|
|
|
|
- }
|
|
|
|
}
|
|
}
|