Skip to content

Commit f874a12

Browse files
committed
Document jwt-bearer authorization grant
Closes gh-9580
1 parent f425c96 commit f874a12

File tree

4 files changed

+227
-5
lines changed

4 files changed

+227
-5
lines changed

config/src/main/resources/org/springframework/security/config/spring-security-5.5.rnc

+2-2
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,8 @@ client-registration.attlist &=
529529
## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients).
530530
attribute client-authentication-method {"client_secret_basic" | "basic" | "client_secret_post" | "post" | "none"}?
531531
client-registration.attlist &=
532-
## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit.
533-
attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit"}?
532+
## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password, implicit, as well as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.
533+
attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit" | "urn:ietf:params:oauth:grant-type:jwt-bearer"}?
534534
client-registration.attlist &=
535535
## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.
536536
attribute redirect-uri {xsd:token}?

config/src/main/resources/org/springframework/security/config/spring-security-5.5.xsd

+3-1
Original file line numberDiff line numberDiff line change
@@ -1673,7 +1673,8 @@
16731673
<xs:attribute name="authorization-grant-type">
16741674
<xs:annotation>
16751675
<xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The
1676-
supported values are authorization_code, client_credentials, password and implicit.
1676+
supported values are authorization_code, client_credentials, password, implicit, as well
1677+
as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.
16771678
</xs:documentation>
16781679
</xs:annotation>
16791680
<xs:simpleType>
@@ -1682,6 +1683,7 @@
16821683
<xs:enumeration value="client_credentials"/>
16831684
<xs:enumeration value="password"/>
16841685
<xs:enumeration value="implicit"/>
1686+
<xs:enumeration value="urn:ietf:params:oauth:grant-type:jwt-bearer"/>
16851687
</xs:restriction>
16861688
</xs:simpleType>
16871689
</xs:attribute>

docs/manual/src/docs/asciidoc/_includes/servlet/appendix/namespace.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,7 @@ The supported values are *client_secret_basic*, *client_secret_post* and *none*
10671067
[[nsa-client-registration-authorization-grant-type]]
10681068
* **authorization-grant-type**
10691069
The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.
1070-
The supported values are `authorization_code`, `client_credentials` and `password`.
1070+
The supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.
10711071

10721072

10731073
[[nsa-client-registration-redirect-uri]]

docs/manual/src/docs/asciidoc/_includes/servlet/oauth2/oauth2-client.adoc

+221-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ At a high-level, the core features available are:
1010
* https://tools.ietf.org/html/rfc6749#section-6[Refresh Token]
1111
* https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials]
1212
* https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials]
13+
* https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[JWT Bearer]
1314

1415
.HTTP Client support
1516
* <<oauth2Client-webclient-servlet, `WebClient` integration for Servlet Environments>> (for requesting protected resources)
@@ -153,6 +154,7 @@ The following sections will go into more detail on the core components used by O
153154
** <<oauth2Client-refresh-token-grant, Refresh Token>>
154155
** <<oauth2Client-client-creds-grant, Client Credentials>>
155156
** <<oauth2Client-password-grant, Resource Owner Password Credentials>>
157+
** <<oauth2Client-jwt-bearer-grant, JWT Bearer>>
156158
* <<oauth2Client-additional-features>>
157159
** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>>
158160
* <<oauth2Client-webclient-servlet>>
@@ -207,7 +209,7 @@ public final class ClientRegistration {
207209
<4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider.
208210
The supported values are *client_secret_basic*, *client_secret_post* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)].
209211
<5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types.
210-
The supported values are `authorization_code`, `client_credentials` and `password`.
212+
The supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`.
211213
<6> `redirectUri`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent
212214
to after the end-user has authenticated and authorized access to the client.
213215
<7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.
@@ -1631,6 +1633,224 @@ class OAuth2ClientController {
16311633
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
16321634

16331635

1636+
[[oauth2Client-jwt-bearer-grant]]
1637+
==== JWT Bearer
1638+
1639+
[NOTE]
1640+
Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the https://datatracker.ietf.org/doc/html/rfc7523[JWT Bearer] grant.
1641+
1642+
1643+
===== Requesting an Access Token
1644+
1645+
[NOTE]
1646+
Please refer to the https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[Access Token Request/Response] protocol flow for the JWT Bearer grant.
1647+
1648+
The default implementation of `OAuth2AccessTokenResponseClient` for the JWT Bearer grant is `DefaultJwtBearerTokenResponseClient`, which uses a `RestOperations` when requesting an access token at the Authorization Server’s Token Endpoint.
1649+
1650+
The `DefaultJwtBearerTokenResponseClient` is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.
1651+
1652+
1653+
===== Customizing the Access Token Request
1654+
1655+
If you need to customize the pre-processing of the Token Request, you can provide `DefaultJwtBearerTokenResponseClient.setRequestEntityConverter()` with a custom `Converter<JwtBearerGrantRequest, RequestEntity<?>>`.
1656+
The default implementation `JwtBearerGrantRequestEntityConverter` builds a `RequestEntity` representation of a https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[OAuth 2.0 Access Token Request].
1657+
However, providing a custom `Converter`, would allow you to extend the Token Request and add custom parameter(s).
1658+
1659+
1660+
===== Customizing the Access Token Response
1661+
1662+
On the other end, if you need to customize the post-handling of the Token Response, you will need to provide `DefaultJwtBearerTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
1663+
The default `RestOperations` is configured as follows:
1664+
1665+
====
1666+
.Java
1667+
[source,java,role="primary"]
1668+
----
1669+
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
1670+
new FormHttpMessageConverter(),
1671+
new OAuth2AccessTokenResponseHttpMessageConverter()));
1672+
1673+
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
1674+
----
1675+
1676+
.Kotlin
1677+
[source,kotlin,role="secondary"]
1678+
----
1679+
val restTemplate = RestTemplate(listOf(
1680+
FormHttpMessageConverter(),
1681+
OAuth2AccessTokenResponseHttpMessageConverter()))
1682+
1683+
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
1684+
----
1685+
====
1686+
1687+
TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request.
1688+
1689+
`OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response.
1690+
You can provide `OAuth2AccessTokenResponseHttpMessageConverter.setTokenResponseConverter()` with a custom `Converter<Map<String, String>, OAuth2AccessTokenResponse>` that is used for converting the OAuth 2.0 Access Token Response parameters to an `OAuth2AccessTokenResponse`.
1691+
1692+
`OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error, eg. 400 Bad Request.
1693+
It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`.
1694+
1695+
Whether you customize `DefaultJwtBearerTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
1696+
1697+
====
1698+
.Java
1699+
[source,java,role="primary"]
1700+
----
1701+
// Customize
1702+
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...
1703+
1704+
JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();
1705+
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
1706+
1707+
OAuth2AuthorizedClientProvider authorizedClientProvider =
1708+
OAuth2AuthorizedClientProviderBuilder.builder()
1709+
.provider(jwtBearerAuthorizedClientProvider)
1710+
.build();
1711+
1712+
...
1713+
1714+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
1715+
----
1716+
1717+
.Kotlin
1718+
[source,kotlin,role="secondary"]
1719+
----
1720+
// Customize
1721+
val jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...
1722+
1723+
val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
1724+
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
1725+
1726+
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
1727+
.provider(jwtBearerAuthorizedClientProvider)
1728+
.build()
1729+
1730+
...
1731+
1732+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
1733+
----
1734+
====
1735+
1736+
===== Using the Access Token
1737+
1738+
Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
1739+
1740+
[source,yaml]
1741+
----
1742+
spring:
1743+
security:
1744+
oauth2:
1745+
client:
1746+
registration:
1747+
okta:
1748+
client-id: okta-client-id
1749+
client-secret: okta-client-secret
1750+
authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
1751+
scope: read
1752+
provider:
1753+
okta:
1754+
token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
1755+
----
1756+
1757+
...and the `OAuth2AuthorizedClientManager` `@Bean`:
1758+
1759+
====
1760+
.Java
1761+
[source,java,role="primary"]
1762+
----
1763+
@Bean
1764+
public OAuth2AuthorizedClientManager authorizedClientManager(
1765+
ClientRegistrationRepository clientRegistrationRepository,
1766+
OAuth2AuthorizedClientRepository authorizedClientRepository) {
1767+
1768+
JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
1769+
new JwtBearerOAuth2AuthorizedClientProvider();
1770+
1771+
OAuth2AuthorizedClientProvider authorizedClientProvider =
1772+
OAuth2AuthorizedClientProviderBuilder.builder()
1773+
.provider(jwtBearerAuthorizedClientProvider)
1774+
.build();
1775+
1776+
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
1777+
new DefaultOAuth2AuthorizedClientManager(
1778+
clientRegistrationRepository, authorizedClientRepository);
1779+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
1780+
1781+
return authorizedClientManager;
1782+
}
1783+
----
1784+
1785+
.Kotlin
1786+
[source,kotlin,role="secondary"]
1787+
----
1788+
@Bean
1789+
fun authorizedClientManager(
1790+
clientRegistrationRepository: ClientRegistrationRepository,
1791+
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
1792+
val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
1793+
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
1794+
.provider(jwtBearerAuthorizedClientProvider)
1795+
.build()
1796+
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
1797+
clientRegistrationRepository, authorizedClientRepository)
1798+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
1799+
return authorizedClientManager
1800+
}
1801+
----
1802+
====
1803+
1804+
You may obtain the `OAuth2AccessToken` as follows:
1805+
1806+
====
1807+
.Java
1808+
[source,java,role="primary"]
1809+
----
1810+
@RestController
1811+
public class OAuth2ResourceServerController {
1812+
1813+
@Autowired
1814+
private OAuth2AuthorizedClientManager authorizedClientManager;
1815+
1816+
@GetMapping("/resource")
1817+
public String resource(JwtAuthenticationToken jwtAuthentication) {
1818+
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
1819+
.principal(jwtAuthentication)
1820+
.build();
1821+
OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
1822+
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
1823+
1824+
...
1825+
1826+
}
1827+
}
1828+
----
1829+
1830+
.Kotlin
1831+
[source,kotlin,role="secondary"]
1832+
----
1833+
class OAuth2ResourceServerController {
1834+
1835+
@Autowired
1836+
private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager
1837+
1838+
@GetMapping("/resource")
1839+
fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
1840+
val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
1841+
.principal(jwtAuthentication)
1842+
.build()
1843+
val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
1844+
val accessToken: OAuth2AccessToken = authorizedClient.accessToken
1845+
1846+
...
1847+
1848+
}
1849+
}
1850+
----
1851+
====
1852+
1853+
16341854
[[oauth2Client-additional-features]]
16351855
=== Additional Features
16361856

0 commit comments

Comments
 (0)