Skip to content

OAuth2ResourceServerSpec should allow its ServerAuthenticationConverter to be configurable #6190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package org.springframework.security.config.web.server;

import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
Expand Down Expand Up @@ -155,10 +159,6 @@
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import static org.springframework.security.web.server.DelegatingServerAuthenticationEntryPoint.DelegateEntry;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.match;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult.notMatch;

/**
* A {@link ServerHttpSecurity} is similar to Spring Security's {@code HttpSecurity} but for WebFlux.
* It allows configuring web based security for specific http requests. By default it will be applied
Expand Down Expand Up @@ -883,6 +883,7 @@ public OAuth2ResourceServerSpec oauth2ResourceServer() {
public class OAuth2ResourceServerSpec {
private ServerAuthenticationEntryPoint entryPoint = new BearerTokenServerAuthenticationEntryPoint();
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();

private JwtSpec jwt;

Expand Down Expand Up @@ -915,6 +916,20 @@ public OAuth2ResourceServerSpec authenticationEntryPoint(ServerAuthenticationEnt
return this;
}

/**
* Configures the {@link ServerAuthenticationConverter} to use for requests authenticating with
* <a href="https://tools.ietf.org/html/rfc6750#section-1.2" target="_blank">Bearer Token</a>s.
*
* @param bearerTokenConverter The {@link ServerAuthenticationConverter} to use
* @return The {@link OAuth2ResourceServerSpec} for additional configuration
* @since 5.2
*/
public OAuth2ResourceServerSpec bearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
this.bearerTokenConverter = bearerTokenConverter;
return this;
}

public JwtSpec jwt() {
if (this.jwt == null) {
this.jwt = new JwtSpec();
Expand Down Expand Up @@ -1003,8 +1018,6 @@ public OAuth2ResourceServerSpec and() {
}

protected void configure(ServerHttpSecurity http) {
ServerBearerTokenAuthenticationConverter bearerTokenConverter =
new ServerBearerTokenAuthenticationConverter();
this.bearerTokenServerWebExchangeMatcher.setBearerTokenConverter(bearerTokenConverter);

registerDefaultAccessDeniedHandler(http);
Expand Down Expand Up @@ -1083,7 +1096,7 @@ private void registerDefaultCsrfOverride(ServerHttpSecurity http) {
}

private class BearerTokenServerWebExchangeMatcher implements ServerWebExchangeMatcher {
ServerBearerTokenAuthenticationConverter bearerTokenConverter;
ServerAuthenticationConverter bearerTokenConverter;

@Override
public Mono<MatchResult> matches(ServerWebExchange exchange) {
Expand All @@ -1092,7 +1105,7 @@ public Mono<MatchResult> matches(ServerWebExchange exchange) {
.onErrorResume(e -> notMatch());
}

public void setBearerTokenConverter(ServerBearerTokenAuthenticationConverter bearerTokenConverter) {
public void setBearerTokenConverter(ServerAuthenticationConverter bearerTokenConverter) {
Assert.notNull(bearerTokenConverter, "bearerTokenConverter cannot be null");
this.bearerTokenConverter = bearerTokenConverter;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
*/
package org.springframework.security.config.web.server;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
Expand All @@ -27,14 +36,14 @@
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.PreDestroy;

import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import org.apache.http.HttpHeaders;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import reactor.core.publisher.Mono;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
Expand All @@ -56,10 +65,12 @@
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
import org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.reactive.server.WebTestClient;
Expand All @@ -70,14 +81,8 @@
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.reactive.config.EnableWebFlux;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;

/**
* Tests for {@link org.springframework.security.config.web.server.ServerHttpSecurity.OAuth2ResourceServerSpec}
Expand Down Expand Up @@ -225,6 +230,16 @@ public void postWhenMissingTokenThenReturnsForbidden() {
.expectStatus().isForbidden();
}

@Test
public void getWhenCustomBearerTokenServerAuthenticationConverterThenResponds() {
this.spring.register(CustomBearerTokenServerAuthenticationConverter.class, RootController.class).autowire();

this.client.get()
.cookie("TOKEN", this.messageReadToken)
.exchange()
.expectStatus().isOk();
}

@Test
public void getWhenSignedAndCustomConverterThenConverts() {
this.spring.register(CustomJwtAuthenticationConverterConfig.class, RootController.class).autowire();
Expand Down Expand Up @@ -429,6 +444,32 @@ ReactiveAuthenticationManager authenticationManager() {
}
}

@EnableWebFlux
@EnableWebFluxSecurity
static class CustomBearerTokenServerAuthenticationConverter {
@Bean
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeExchange()
.anyExchange().hasAuthority("SCOPE_message:read")
.and()
.oauth2ResourceServer()
.bearerTokenConverter(bearerTokenAuthenticationConverter())
.jwt()
.publicKey(publicKey());
// @formatter:on

return http.build();
}

@Bean
ServerAuthenticationConverter bearerTokenAuthenticationConverter() {
return exchange -> Mono.justOrEmpty(exchange.getRequest().getCookies().getFirst("TOKEN").getValue())
.map(BearerTokenAuthenticationToken::new);
}
}

@EnableWebFlux
@EnableWebFluxSecurity
static class CustomJwtAuthenticationConverterConfig {
Expand Down