OAuth2AuthorizationCodeGrantWebFilter works with /{action}/

This ensures that the same URL can work for both log in and
authorization code which prevents having to create additional registrations
on the client and potentially on the server (GitHub only allows a single
valid redirect URL).

Fixes: gh-5856
This commit is contained in:
Rob Winch 2018-09-14 11:11:51 -05:00
parent 26afc18b66
commit 385bdfc055
3 changed files with 12 additions and 3 deletions

View File

@ -48,6 +48,7 @@ public enum SecurityWebFiltersOrder {
*/ */
FORM_LOGIN, FORM_LOGIN,
AUTHENTICATION, AUTHENTICATION,
OAUTH2_AUTHORIZATION_CODE,
LOGIN_PAGE_GENERATING, LOGIN_PAGE_GENERATING,
LOGOUT_PAGE_GENERATING, LOGOUT_PAGE_GENERATING,
/** /**

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.util.context.Context; import reactor.util.context.Context;
@ -552,7 +553,7 @@ public class ServerHttpSecurity {
} }
AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository); AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, authorizedClientRepository);
authenticationFilter.setRequiresAuthenticationMatcher(new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}")); authenticationFilter.setRequiresAuthenticationMatcher(createAttemptAuthenticationRequestMatcher());
authenticationFilter.setServerAuthenticationConverter(new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository)); authenticationFilter.setServerAuthenticationConverter(new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository));
RedirectServerAuthenticationSuccessHandler redirectHandler = new RedirectServerAuthenticationSuccessHandler(); RedirectServerAuthenticationSuccessHandler redirectHandler = new RedirectServerAuthenticationSuccessHandler();
@ -581,6 +582,13 @@ public class ServerHttpSecurity {
http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION); http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
} }
private ServerWebExchangeMatcher createAttemptAuthenticationRequestMatcher() {
PathPatternParserServerWebExchangeMatcher loginPathMatcher = new PathPatternParserServerWebExchangeMatcher("/login/oauth2/code/{registrationId}");
ServerWebExchangeMatcher notAuthenticatedMatcher = e -> ReactiveSecurityContextHolder.getContext()
.flatMap(p -> ServerWebExchangeMatcher.MatchResult.notMatch())
.switchIfEmpty(ServerWebExchangeMatcher.MatchResult.match());
return new AndServerWebExchangeMatcher(loginPathMatcher, notAuthenticatedMatcher);
}
private Map<String, String> getLinks() { private Map<String, String> getLinks() {
Iterable<ClientRegistration> registrations = getBeanOrNull(ResolvableType.forClassWithGenerics(Iterable.class, ClientRegistration.class)); Iterable<ClientRegistration> registrations = getBeanOrNull(ResolvableType.forClassWithGenerics(Iterable.class, ClientRegistration.class));
if (registrations == null) { if (registrations == null) {
@ -686,7 +694,7 @@ public class ServerHttpSecurity {
OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter( OAuth2AuthorizationRequestRedirectWebFilter oauthRedirectFilter = new OAuth2AuthorizationRequestRedirectWebFilter(
clientRegistrationRepository); clientRegistrationRepository);
http.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.AUTHENTICATION); http.addFilterAt(codeGrantWebFilter, SecurityWebFiltersOrder.OAUTH2_AUTHORIZATION_CODE);
http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC); http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC);
} }

View File

@ -109,7 +109,7 @@ public class OAuth2AuthorizationCodeGrantWebFilter implements WebFilter {
Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null"); Assert.notNull(authorizedClientRepository, "authorizedClientRepository cannot be null");
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
this.authorizedClientRepository = authorizedClientRepository; this.authorizedClientRepository = authorizedClientRepository;
this.requiresAuthenticationMatcher = new PathPatternParserServerWebExchangeMatcher("/authorize/oauth2/code/{registrationId}"); this.requiresAuthenticationMatcher = new PathPatternParserServerWebExchangeMatcher("/{action}/oauth2/code/{registrationId}");
this.authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository); this.authenticationConverter = new ServerOAuth2AuthorizationCodeAuthenticationTokenConverter(clientRegistrationRepository);
this.authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler(); this.authenticationSuccessHandler = new RedirectServerAuthenticationSuccessHandler();
this.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception); this.authenticationFailureHandler = (webFilterExchange, exception) -> Mono.error(exception);