By default, Resource Server looks for a bearer token in the Authorization
header.
This, however, can be customized in a handful of ways.
For example, you may have a need to read the bearer token from a custom header.
To achieve this, you can expose a DefaultBearerTokenResolver
as a bean, or wire an instance into the DSL, as you can see in the following example:
- Java
-
@Bean BearerTokenResolver bearerTokenResolver() { DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver(); bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION); return bearerTokenResolver; }
- Kotlin
-
@Bean fun bearerTokenResolver(): BearerTokenResolver { val bearerTokenResolver = DefaultBearerTokenResolver() bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION) return bearerTokenResolver }
- Xml
-
<http> <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/> </http> <bean id="bearerTokenResolver" class="org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver"> <property name="bearerTokenHeaderName" value="Proxy-Authorization"/> </bean>
Or, in circumstances where a provider is using both a custom header and value, you can use HeaderBearerTokenResolver
instead.
Or, you may wish to read the token from a form parameter, which you can do by configuring the DefaultBearerTokenResolver
, as you can see below:
- Java
-
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver(); resolver.setAllowFormEncodedBodyParameter(true); http .oauth2ResourceServer(oauth2 -> oauth2 .bearerTokenResolver(resolver) );
- Kotlin
-
val resolver = DefaultBearerTokenResolver() resolver.setAllowFormEncodedBodyParameter(true) http { oauth2ResourceServer { bearerTokenResolver = resolver } }
- Xml
-
<http> <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/> </http> <bean id="bearerTokenResolver" class="org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver"> <property name="allowFormEncodedBodyParameter" value="true"/> </bean>
Now that your resource server has validated the token, it might be handy to pass it to downstream services.
This is quite simple with {security-api-url}org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.html[ServletBearerExchangeFilterFunction]
, which you can see in the following example:
- Java
-
@Bean public WebClient rest() { return WebClient.builder() .filter(new ServletBearerExchangeFilterFunction()) .build(); }
- Kotlin
-
@Bean fun rest(): WebClient { return WebClient.builder() .filter(ServletBearerExchangeFilterFunction()) .build() }
When the above WebClient
is used to perform requests, Spring Security will look up the current Authentication
and extract any {security-api-url}org/springframework/security/oauth2/core/AbstractOAuth2Token.html[AbstractOAuth2Token]
credential.
Then, it will propagate that token in the Authorization
header.
For example:
- Java
-
this.rest.get() .uri("https://other-service.example.com/endpoint") .retrieve() .bodyToMono(String.class) .block()
- Kotlin
-
this.rest.get() .uri("https://other-service.example.com/endpoint") .retrieve() .bodyToMono<String>() .block()
Will invoke the https://other-service.example.com/endpoint
, adding the bearer token Authorization
header for you.
In places where you need to override this behavior, it’s a simple matter of supplying the header yourself, like so:
- Java
-
this.rest.get() .uri("https://other-service.example.com/endpoint") .headers(headers -> headers.setBearerAuth(overridingToken)) .retrieve() .bodyToMono(String.class) .block()
- Kotlin
-
this.rest.get() .uri("https://other-service.example.com/endpoint") .headers{ headers -> headers.setBearerAuth(overridingToken)} .retrieve() .bodyToMono<String>() .block()
In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
Note
|
Unlike the {security-api-url}org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.html[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired. To obtain this level of support, please use the OAuth 2.0 Client filter. |
There is no RestTemplate
equivalent for ServletBearerExchangeFilterFunction
at the moment, but you can propagate the request’s bearer token quite simply with your own interceptor:
- Java
-
@Bean RestTemplate rest() { RestTemplate rest = new RestTemplate(); rest.getInterceptors().add((request, body, execution) -> { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { return execution.execute(request, body); } if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) { return execution.execute(request, body); } AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials(); request.getHeaders().setBearerAuth(token.getTokenValue()); return execution.execute(request, body); }); return rest; }
- Kotlin
-
@Bean fun rest(): RestTemplate { val rest = RestTemplate() rest.interceptors.add(ClientHttpRequestInterceptor { request, body, execution -> val authentication: Authentication? = SecurityContextHolder.getContext().authentication if (authentication == null) { return execution.execute(request, body) } if (authentication.credentials !is AbstractOAuth2Token) { return execution.execute(request, body) } request.headers.setBearerAuth(authentication.credentials.tokenValue) execution.execute(request, body) }) return rest }
Note
|
Unlike the {security-api-url}org/springframework/security/oauth2/client/OAuth2AuthorizedClientManager.html[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired. To obtain this level of support, please create an interceptor using the OAuth 2.0 Authorized Client Manager. |
A bearer token may be invalid for a number of reasons. For example, the token may no longer be active.
In these circumstances, Resource Server throws an InvalidBearerTokenException
.
Like other exceptions, this results in an OAuth 2.0 Bearer Token error response:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error_code="invalid_token", error_description="Unsupported algorithm of none", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
Additionally, it is published as an AuthenticationFailureBadCredentialsEvent
, which you can listen for in your application like so:
- Java
-
@Component public class FailureEvents { @EventListener public void onFailure(AuthenticationFailureBadCredentialsEvent badCredentials) { if (badCredentials.getAuthentication() instanceof BearerTokenAuthenticationToken) { // ... handle } } }
- Kotlin
-
@Component class FailureEvents { @EventListener fun onFailure(badCredentials: AuthenticationFailureBadCredentialsEvent) { if (badCredentials.authentication is BearerTokenAuthenticationToken) { // ... handle } } }