2
0

bearer-tokens.adoc 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. = OAuth 2.0 Bearer Tokens
  2. [[oauth2resourceserver-bearertoken-resolver]]
  3. == Bearer Token Resolution
  4. By default, Resource Server looks for a bearer token in the `Authorization` header.
  5. This, however, can be customized in a handful of ways.
  6. === Reading the Bearer Token from a Custom Header
  7. For example, you may have a need to read the bearer token from a custom header.
  8. 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:
  9. .Custom Bearer Token Header
  10. ====
  11. .Java
  12. [source,java,role="primary"]
  13. ----
  14. @Bean
  15. BearerTokenResolver bearerTokenResolver() {
  16. DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
  17. bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION);
  18. return bearerTokenResolver;
  19. }
  20. ----
  21. .Kotlin
  22. [source,kotlin,role="secondary"]
  23. ----
  24. @Bean
  25. fun bearerTokenResolver(): BearerTokenResolver {
  26. val bearerTokenResolver = DefaultBearerTokenResolver()
  27. bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION)
  28. return bearerTokenResolver
  29. }
  30. ----
  31. .Xml
  32. [source,xml,role="secondary"]
  33. ----
  34. <http>
  35. <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
  36. </http>
  37. <bean id="bearerTokenResolver"
  38. class="org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver">
  39. <property name="bearerTokenHeaderName" value="Proxy-Authorization"/>
  40. </bean>
  41. ----
  42. ====
  43. Or, in circumstances where a provider is using both a custom header and value, you can use `HeaderBearerTokenResolver` instead.
  44. === Reading the Bearer Token from a Form Parameter
  45. 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:
  46. .Form Parameter Bearer Token
  47. ====
  48. .Java
  49. [source,java,role="primary"]
  50. ----
  51. DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
  52. resolver.setAllowFormEncodedBodyParameter(true);
  53. http
  54. .oauth2ResourceServer(oauth2 -> oauth2
  55. .bearerTokenResolver(resolver)
  56. );
  57. ----
  58. .Kotlin
  59. [source,kotlin,role="secondary"]
  60. ----
  61. val resolver = DefaultBearerTokenResolver()
  62. resolver.setAllowFormEncodedBodyParameter(true)
  63. http {
  64. oauth2ResourceServer {
  65. bearerTokenResolver = resolver
  66. }
  67. }
  68. ----
  69. .Xml
  70. [source,xml,role="secondary"]
  71. ----
  72. <http>
  73. <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
  74. </http>
  75. <bean id="bearerTokenResolver"
  76. class="org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver">
  77. <property name="allowFormEncodedBodyParameter" value="true"/>
  78. </bean>
  79. ----
  80. ====
  81. == Bearer Token Propagation
  82. Now that your resource server has validated the token, it might be handy to pass it to downstream services.
  83. 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:
  84. ====
  85. .Java
  86. [source,java,role="primary"]
  87. ----
  88. @Bean
  89. public WebClient rest() {
  90. return WebClient.builder()
  91. .filter(new ServletBearerExchangeFilterFunction())
  92. .build();
  93. }
  94. ----
  95. .Kotlin
  96. [source,kotlin,role="secondary"]
  97. ----
  98. @Bean
  99. fun rest(): WebClient {
  100. return WebClient.builder()
  101. .filter(ServletBearerExchangeFilterFunction())
  102. .build()
  103. }
  104. ----
  105. ====
  106. 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.
  107. Then, it will propagate that token in the `Authorization` header.
  108. For example:
  109. ====
  110. .Java
  111. [source,java,role="primary"]
  112. ----
  113. this.rest.get()
  114. .uri("https://other-service.example.com/endpoint")
  115. .retrieve()
  116. .bodyToMono(String.class)
  117. .block()
  118. ----
  119. .Kotlin
  120. [source,kotlin,role="secondary"]
  121. ----
  122. this.rest.get()
  123. .uri("https://other-service.example.com/endpoint")
  124. .retrieve()
  125. .bodyToMono<String>()
  126. .block()
  127. ----
  128. ====
  129. Will invoke the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.
  130. In places where you need to override this behavior, it's a simple matter of supplying the header yourself, like so:
  131. ====
  132. .Java
  133. [source,java,role="primary"]
  134. ----
  135. this.rest.get()
  136. .uri("https://other-service.example.com/endpoint")
  137. .headers(headers -> headers.setBearerAuth(overridingToken))
  138. .retrieve()
  139. .bodyToMono(String.class)
  140. .block()
  141. ----
  142. .Kotlin
  143. [source,kotlin,role="secondary"]
  144. ----
  145. this.rest.get()
  146. .uri("https://other-service.example.com/endpoint")
  147. .headers{ headers -> headers.setBearerAuth(overridingToken)}
  148. .retrieve()
  149. .bodyToMono<String>()
  150. .block()
  151. ----
  152. ====
  153. In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
  154. [NOTE]
  155. 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.
  156. To obtain this level of support, please use the OAuth 2.0 Client filter.
  157. === `RestTemplate` support
  158. 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:
  159. ====
  160. .Java
  161. [source,java,role="primary"]
  162. ----
  163. @Bean
  164. RestTemplate rest() {
  165. RestTemplate rest = new RestTemplate();
  166. rest.getInterceptors().add((request, body, execution) -> {
  167. Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  168. if (authentication == null) {
  169. return execution.execute(request, body);
  170. }
  171. if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
  172. return execution.execute(request, body);
  173. }
  174. AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
  175. request.getHeaders().setBearerAuth(token.getTokenValue());
  176. return execution.execute(request, body);
  177. });
  178. return rest;
  179. }
  180. ----
  181. .Kotlin
  182. [source,kotlin,role="secondary"]
  183. ----
  184. @Bean
  185. fun rest(): RestTemplate {
  186. val rest = RestTemplate()
  187. rest.interceptors.add(ClientHttpRequestInterceptor { request, body, execution ->
  188. val authentication: Authentication? = SecurityContextHolder.getContext().authentication
  189. if (authentication != null) {
  190. execution.execute(request, body)
  191. }
  192. if (authentication!!.credentials !is AbstractOAuth2Token) {
  193. execution.execute(request, body)
  194. }
  195. val token: AbstractOAuth2Token = authentication.credentials as AbstractOAuth2Token
  196. request.headers.setBearerAuth(token.tokenValue)
  197. execution.execute(request, body)
  198. })
  199. return rest
  200. }
  201. ----
  202. ====
  203. [NOTE]
  204. 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.
  205. To obtain this level of support, please create an interceptor using the xref:servlet/oauth2/client/index.adoc#oauth2client[OAuth 2.0 Authorized Client Manager].
  206. [[oauth2resourceserver-bearertoken-failure]]
  207. == Bearer Token Failure
  208. A bearer token may be invalid for a number of reasons. For example, the token may no longer be active.
  209. In these circumstances, Resource Server throws an `InvalidBearerTokenException`.
  210. Like other exceptions, this results in an OAuth 2.0 Bearer Token error response:
  211. [source,http request]
  212. ----
  213. HTTP/1.1 401 Unauthorized
  214. WWW-Authenticate: Bearer error_code="invalid_token", error_description="Unsupported algorithm of none", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
  215. ----
  216. Additionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can xref:servlet/authentication/events.adoc#servlet-events[listen for in your application] like so:
  217. ====
  218. .Java
  219. [source,java,role="primary"]
  220. ----
  221. @Component
  222. public class FailureEvents {
  223. @EventListener
  224. public void onFailure(AuthenticationFailureBadCredentialsEvent badCredentials) {
  225. if (badCredentials.getAuthentication() instanceof BearerTokenAuthenticationToken) {
  226. // ... handle
  227. }
  228. }
  229. }
  230. ----
  231. .Kotlin
  232. [source,kotlin,role="secondary"]
  233. ----
  234. @Component
  235. class FailureEvents {
  236. @EventListener
  237. fun onFailure(badCredentials: AuthenticationFailureBadCredentialsEvent) {
  238. if (badCredentials.authentication is BearerTokenAuthenticationToken) {
  239. // ... handle
  240. }
  241. }
  242. }
  243. ----
  244. ====