3

I am creating an application based on this example -

Background -

https://github.com/spring-projects/spring-security/tree/master/samples/boot/oauth2resourceserver-webflux

It works perfectly fine of the OAuth2 token is in the Header.

Problem -

However I would like to change it to use an OAuth 2 token in the url. I am trying to create a OAuth2 resource server.

Analysis-

It seems Spring Security supports getting the token from access_token parameter -

https://github.com/spring-projects/spring-security/blob/e3eaa99ad06769cf44ad3e1249f6398077b90834/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java#L57

However it seems to be disabled by default -

https://github.com/spring-projects/spring-security/blob/master/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/ServerBearerTokenAuthenticationConverter.java#L48

Now this class is not accessible outside the spring hierarchy is directly created here -

https://github.com/spring-projects/spring-security/blob/master/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java#L955

Question?

Is there a to set this allowUriQueryParameter to true in my code?

Update

I am creating a OAuth2 resource server. Unfortunately the OAuth2ResourceServerSpec does not allow authenticationConverter to be set.

Pushkar
  • 7,450
  • 10
  • 38
  • 57

5 Answers5

6

The Pushkar answer didn't work for me but helped me to find the solution, the following code did the trick:

DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
resolver.setAllowUriQueryParameter(true);

http.authorizeRequests()
        .anyRequest().authenticated()
        .and().oauth2ResourceServer().bearerTokenResolver(resolver)
        .jwt();

Thanks.

João Durante
  • 273
  • 5
  • 14
  • In the current version of Spring today, this is absolutely the right answer. The `DefaultBearerTokenResolver` shares a lot of code with the `ServerBearerTokenAuthenticationConverter` and seems to be the modern-day replacement. – Mark Tielemans Oct 19 '21 at 12:43
4

Now with Spring Security 5.1.5 we can do this -

ServerBearerTokenAuthenticationConverter 
authenticationConverter = new ServerBearerTokenAuthenticationConverter();
authenticationConverter.setAllowUriQueryParameter(true);

http.oauth2ResourceServer().bearerTokenConverter(authenticationConverter).jwt();
Pushkar
  • 7,450
  • 10
  • 38
  • 57
  • I'm using Spring Security 5.3.3 and the last line of this snippet shows red for bearerTokenConverter in oauth2ResourceServer, as it has no such member. Do you know what I could do? Thank you. – Cécile Fecherolle Oct 14 '20 at 08:12
2

You can check official docs for Spring Security

You can create new ServerBearerTokenAuthenticationConverter(), set allowUriQueryParameter a register in ServerHttpSecurity.

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .oauth2Login()
            .authenticationConverter(converter) // set 
            .authenticationManager(manager)
            .authorizedClientRepository(authorizedClients)
            .clientRegistrationRepository(clientRegistrations);
    return http.build();
}
Matej Marconak
  • 1,365
  • 3
  • 20
  • 25
  • 1
    I am trying to create a resource server. Unfortunately the OAuth2ResourceServerSpec does not allow authenticationConverter to be set. – Pushkar Sep 27 '18 at 20:47
  • You can't, basically. The only way (as of Spring 5 and WebFlux 5.1.3 at least) is to write a WebFilter and convert the query parameter to a header. – thepanuto Mar 22 '19 at 16:58
1

Spring MVC has a lot of sensible defaults that are lacking in WebFlux. This is one of them.

I worked around this issue by setting up a WebFilter that takes tokens in the query string and add them to the header.

@Override
@NonNull
public Mono<Void> filter(
  @NonNull ServerWebExchange serverWebExchange, @NonNull WebFilterChain 
     webFilterChain) {
  ServerHttpRequest request = serverWebExchange.getRequest();
  return webFilterChain.filter(
    serverWebExchange.mutate().request(sanitizeRequest(request)).build());
}

private ServerHttpRequest sanitizeRequest(ServerHttpRequest request) {
  MultiValueMap<String, String> queryParams = request.getQueryParams();
  ServerHttpRequest.Builder newRequest =
    request
        .mutate()
        .headers(
            headers -> {
              if (headerContainsAuthorizationKey(headers)) {
                String token = getAuthorizationBearer(headers);
                if (token != null) {
                  // "bearer" breaks WebFlux OAuth :)
                  headers.set(HttpHeaders.AUTHORIZATION, token.replace("bearer", "Bearer"));
                  return;
                }
              }
              if (pathContainsAccessToken(queryParams)) {
                headers.set(
                    HttpHeaders.AUTHORIZATION,
                    "Bearer " + queryParams.getFirst("access_token"));
              }
            });

  if (pathContainsAccessToken(queryParams)) {
    newRequest.uri(buildNewUriWithoutToken(request.getURI(), queryParams));
  }
  return newRequest.build();
 }
}

It's nice pointing out that the lowercase "bearer" breaks WebFlux as well.

After that, you can add this filter right before your oauthResourceServer spec in the security chain:

@Bean
public SecurityWebFilterChain springSecurityFilterChain(
  ServerHttpSecurity http,
  TokenStore tokenStore,
  AuthorizationHeaderFilter authorizationHeaderFilter) {
    http.csrf()
      .disable()
      .authorizeExchange()
      .anyExchange()
      .authenticated()
      .and()
      .addFilterAt(authorizationHeaderFilter, SecurityWebFiltersOrder.FIRST)
      .oauth2ResourceServer()
      .jwt() // ...  etc
  return http.build();
}
thepanuto
  • 783
  • 5
  • 17
0

As a walkaround, you can write a WebFilter to convert the Query Parameter to a Header.