mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	Document jwt-bearer authorization grant
Closes gh-9580
This commit is contained in:
		
							parent
							
								
									f425c96f06
								
							
						
					
					
						commit
						f874a12ddb
					
				| @ -529,8 +529,8 @@ client-registration.attlist &= | |||||||
| 	## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients). | 	## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients). | ||||||
| 	attribute client-authentication-method {"client_secret_basic" | "basic" | "client_secret_post" | "post" | "none"}? | 	attribute client-authentication-method {"client_secret_basic" | "basic" | "client_secret_post" | "post" | "none"}? | ||||||
| client-registration.attlist &= | client-registration.attlist &= | ||||||
| 	## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password and implicit. | 	## 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. | ||||||
| 	attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit"}? | 	attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password" | "implicit" | "urn:ietf:params:oauth:grant-type:jwt-bearer"}? | ||||||
| client-registration.attlist &= | client-registration.attlist &= | ||||||
| 	## 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. | 	## 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. | ||||||
| 	attribute redirect-uri {xsd:token}? | 	attribute redirect-uri {xsd:token}? | ||||||
|  | |||||||
| @ -1673,7 +1673,8 @@ | |||||||
|       <xs:attribute name="authorization-grant-type"> |       <xs:attribute name="authorization-grant-type"> | ||||||
|          <xs:annotation> |          <xs:annotation> | ||||||
|             <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The |             <xs:documentation>The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The | ||||||
|                 supported values are authorization_code, client_credentials, password and implicit. |                 supported values are authorization_code, client_credentials, password, implicit, as well | ||||||
|  |                 as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer. | ||||||
|                 </xs:documentation> |                 </xs:documentation> | ||||||
|          </xs:annotation> |          </xs:annotation> | ||||||
|          <xs:simpleType> |          <xs:simpleType> | ||||||
| @ -1682,6 +1683,7 @@ | |||||||
|                <xs:enumeration value="client_credentials"/> |                <xs:enumeration value="client_credentials"/> | ||||||
|                <xs:enumeration value="password"/> |                <xs:enumeration value="password"/> | ||||||
|                <xs:enumeration value="implicit"/> |                <xs:enumeration value="implicit"/> | ||||||
|  |                <xs:enumeration value="urn:ietf:params:oauth:grant-type:jwt-bearer"/> | ||||||
|             </xs:restriction> |             </xs:restriction> | ||||||
|          </xs:simpleType> |          </xs:simpleType> | ||||||
|       </xs:attribute> |       </xs:attribute> | ||||||
|  | |||||||
| @ -1067,7 +1067,7 @@ The supported values are *client_secret_basic*, *client_secret_post* and *none* | |||||||
| [[nsa-client-registration-authorization-grant-type]] | [[nsa-client-registration-authorization-grant-type]] | ||||||
| * **authorization-grant-type** | * **authorization-grant-type** | ||||||
| The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types. | The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types. | ||||||
| The supported values are `authorization_code`, `client_credentials` and `password`. | The supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| [[nsa-client-registration-redirect-uri]] | [[nsa-client-registration-redirect-uri]] | ||||||
|  | |||||||
| @ -10,6 +10,7 @@ At a high-level, the core features available are: | |||||||
| * https://tools.ietf.org/html/rfc6749#section-6[Refresh Token] | * https://tools.ietf.org/html/rfc6749#section-6[Refresh Token] | ||||||
| * https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] | * https://tools.ietf.org/html/rfc6749#section-1.3.4[Client Credentials] | ||||||
| * https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials] | * https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials] | ||||||
|  | * https://datatracker.ietf.org/doc/html/rfc7523#section-2.1[JWT Bearer] | ||||||
| 
 | 
 | ||||||
| .HTTP Client support | .HTTP Client support | ||||||
| * <<oauth2Client-webclient-servlet, `WebClient` integration for Servlet Environments>> (for requesting protected resources) | * <<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 | |||||||
| ** <<oauth2Client-refresh-token-grant, Refresh Token>> | ** <<oauth2Client-refresh-token-grant, Refresh Token>> | ||||||
| ** <<oauth2Client-client-creds-grant, Client Credentials>> | ** <<oauth2Client-client-creds-grant, Client Credentials>> | ||||||
| ** <<oauth2Client-password-grant, Resource Owner Password Credentials>> | ** <<oauth2Client-password-grant, Resource Owner Password Credentials>> | ||||||
|  | ** <<oauth2Client-jwt-bearer-grant, JWT Bearer>> | ||||||
| * <<oauth2Client-additional-features>> | * <<oauth2Client-additional-features>> | ||||||
| ** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>> | ** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>> | ||||||
| * <<oauth2Client-webclient-servlet>> | * <<oauth2Client-webclient-servlet>> | ||||||
| @ -207,7 +209,7 @@ public final class ClientRegistration { | |||||||
| <4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider. | <4> `clientAuthenticationMethod`: The method used to authenticate the Client with the Provider. | ||||||
| The supported values are *client_secret_basic*, *client_secret_post* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)]. | The supported values are *client_secret_basic*, *client_secret_post* and *none* https://tools.ietf.org/html/rfc6749#section-2.1[(public clients)]. | ||||||
| <5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types. | <5> `authorizationGrantType`: The OAuth 2.0 Authorization Framework defines four https://tools.ietf.org/html/rfc6749#section-1.3[Authorization Grant] types. | ||||||
|  The supported values are `authorization_code`, `client_credentials` and `password`. |  The supported values are `authorization_code`, `client_credentials`, `password`, as well as, extension grant type `urn:ietf:params:oauth:grant-type:jwt-bearer`. | ||||||
| <6> `redirectUri`: The client's registered redirect URI that the _Authorization Server_ redirects the end-user's user-agent | <6> `redirectUri`: 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. |  to after the end-user has authenticated and authorized access to the client. | ||||||
| <7> `scopes`: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile. | <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 { | |||||||
| If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`. | If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | [[oauth2Client-jwt-bearer-grant]] | ||||||
|  | ==== JWT Bearer | ||||||
|  | 
 | ||||||
|  | [NOTE] | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ===== Requesting an Access Token | ||||||
|  | 
 | ||||||
|  | [NOTE] | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | 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. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ===== Customizing the Access Token Request | ||||||
|  | 
 | ||||||
|  | If you need to customize the pre-processing of the Token Request, you can provide `DefaultJwtBearerTokenResponseClient.setRequestEntityConverter()` with a custom `Converter<JwtBearerGrantRequest, RequestEntity<?>>`. | ||||||
|  | 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]. | ||||||
|  | However, providing a custom `Converter`, would allow you to extend the Token Request and add custom parameter(s). | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ===== Customizing the Access Token Response | ||||||
|  | 
 | ||||||
|  | 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`. | ||||||
|  | The default `RestOperations` is configured as follows: | ||||||
|  | 
 | ||||||
|  | ==== | ||||||
|  | .Java | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | RestTemplate restTemplate = new RestTemplate(Arrays.asList( | ||||||
|  | 		new FormHttpMessageConverter(), | ||||||
|  | 		new OAuth2AccessTokenResponseHttpMessageConverter())); | ||||||
|  | 
 | ||||||
|  | restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Kotlin | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | val restTemplate = RestTemplate(listOf( | ||||||
|  |         FormHttpMessageConverter(), | ||||||
|  |         OAuth2AccessTokenResponseHttpMessageConverter())) | ||||||
|  | 
 | ||||||
|  | restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler() | ||||||
|  | ---- | ||||||
|  | ==== | ||||||
|  | 
 | ||||||
|  | TIP: Spring MVC `FormHttpMessageConverter` is required as it's used when sending the OAuth 2.0 Access Token Request. | ||||||
|  | 
 | ||||||
|  | `OAuth2AccessTokenResponseHttpMessageConverter` is a `HttpMessageConverter` for an OAuth 2.0 Access Token Response. | ||||||
|  | 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`. | ||||||
|  | 
 | ||||||
|  | `OAuth2ErrorResponseErrorHandler` is a `ResponseErrorHandler` that can handle an OAuth 2.0 Error, eg. 400 Bad Request. | ||||||
|  | It uses an `OAuth2ErrorHttpMessageConverter` for converting the OAuth 2.0 Error parameters to an `OAuth2Error`. | ||||||
|  | 
 | ||||||
|  | Whether you customize `DefaultJwtBearerTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example: | ||||||
|  | 
 | ||||||
|  | ==== | ||||||
|  | .Java | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | // Customize | ||||||
|  | OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ... | ||||||
|  | 
 | ||||||
|  | JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider(); | ||||||
|  | jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient); | ||||||
|  | 
 | ||||||
|  | OAuth2AuthorizedClientProvider authorizedClientProvider = | ||||||
|  | 		OAuth2AuthorizedClientProviderBuilder.builder() | ||||||
|  | 				.provider(jwtBearerAuthorizedClientProvider) | ||||||
|  | 				.build(); | ||||||
|  | 
 | ||||||
|  | ... | ||||||
|  | 
 | ||||||
|  | authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Kotlin | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | // Customize | ||||||
|  | val jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ... | ||||||
|  | 
 | ||||||
|  | val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider() | ||||||
|  | jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient); | ||||||
|  | 
 | ||||||
|  | val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() | ||||||
|  |         .provider(jwtBearerAuthorizedClientProvider) | ||||||
|  |         .build() | ||||||
|  | 
 | ||||||
|  | ... | ||||||
|  | 
 | ||||||
|  | authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) | ||||||
|  | ---- | ||||||
|  | ==== | ||||||
|  | 
 | ||||||
|  | ===== Using the Access Token | ||||||
|  | 
 | ||||||
|  | Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration: | ||||||
|  | 
 | ||||||
|  | [source,yaml] | ||||||
|  | ---- | ||||||
|  | spring: | ||||||
|  |   security: | ||||||
|  |     oauth2: | ||||||
|  |       client: | ||||||
|  |         registration: | ||||||
|  |           okta: | ||||||
|  |             client-id: okta-client-id | ||||||
|  |             client-secret: okta-client-secret | ||||||
|  |             authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer | ||||||
|  |             scope: read | ||||||
|  |         provider: | ||||||
|  |           okta: | ||||||
|  |             token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | ...and the `OAuth2AuthorizedClientManager` `@Bean`: | ||||||
|  | 
 | ||||||
|  | ==== | ||||||
|  | .Java | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | @Bean | ||||||
|  | public OAuth2AuthorizedClientManager authorizedClientManager( | ||||||
|  | 		ClientRegistrationRepository clientRegistrationRepository, | ||||||
|  | 		OAuth2AuthorizedClientRepository authorizedClientRepository) { | ||||||
|  | 
 | ||||||
|  | 	JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = | ||||||
|  | 			new JwtBearerOAuth2AuthorizedClientProvider(); | ||||||
|  | 
 | ||||||
|  | 	OAuth2AuthorizedClientProvider authorizedClientProvider = | ||||||
|  | 			OAuth2AuthorizedClientProviderBuilder.builder() | ||||||
|  | 					.provider(jwtBearerAuthorizedClientProvider) | ||||||
|  | 					.build(); | ||||||
|  | 
 | ||||||
|  | 	DefaultOAuth2AuthorizedClientManager authorizedClientManager = | ||||||
|  | 			new DefaultOAuth2AuthorizedClientManager( | ||||||
|  | 					clientRegistrationRepository, authorizedClientRepository); | ||||||
|  | 	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); | ||||||
|  | 
 | ||||||
|  | 	return authorizedClientManager; | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Kotlin | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | @Bean | ||||||
|  | fun authorizedClientManager( | ||||||
|  |         clientRegistrationRepository: ClientRegistrationRepository, | ||||||
|  |         authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager { | ||||||
|  |     val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider() | ||||||
|  |     val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() | ||||||
|  |             .provider(jwtBearerAuthorizedClientProvider) | ||||||
|  |             .build() | ||||||
|  |     val authorizedClientManager = DefaultOAuth2AuthorizedClientManager( | ||||||
|  |             clientRegistrationRepository, authorizedClientRepository) | ||||||
|  |     authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) | ||||||
|  |     return authorizedClientManager | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | ==== | ||||||
|  | 
 | ||||||
|  | You may obtain the `OAuth2AccessToken` as follows: | ||||||
|  | 
 | ||||||
|  | ==== | ||||||
|  | .Java | ||||||
|  | [source,java,role="primary"] | ||||||
|  | ---- | ||||||
|  | @RestController | ||||||
|  | public class OAuth2ResourceServerController { | ||||||
|  | 
 | ||||||
|  | 	@Autowired | ||||||
|  | 	private OAuth2AuthorizedClientManager authorizedClientManager; | ||||||
|  | 
 | ||||||
|  | 	@GetMapping("/resource") | ||||||
|  | 	public String resource(JwtAuthenticationToken jwtAuthentication) { | ||||||
|  | 		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta") | ||||||
|  | 				.principal(jwtAuthentication) | ||||||
|  | 				.build(); | ||||||
|  | 		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); | ||||||
|  | 		OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); | ||||||
|  | 
 | ||||||
|  | 		... | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Kotlin | ||||||
|  | [source,kotlin,role="secondary"] | ||||||
|  | ---- | ||||||
|  | class OAuth2ResourceServerController { | ||||||
|  | 
 | ||||||
|  |     @Autowired | ||||||
|  |     private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager | ||||||
|  | 
 | ||||||
|  |     @GetMapping("/resource") | ||||||
|  |     fun resource(jwtAuthentication: JwtAuthenticationToken?): String { | ||||||
|  |         val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta") | ||||||
|  |                 .principal(jwtAuthentication) | ||||||
|  |                 .build() | ||||||
|  |         val authorizedClient = authorizedClientManager.authorize(authorizeRequest) | ||||||
|  |         val accessToken: OAuth2AccessToken = authorizedClient.accessToken | ||||||
|  | 
 | ||||||
|  |         ... | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ---- | ||||||
|  | ==== | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| [[oauth2Client-additional-features]] | [[oauth2Client-additional-features]] | ||||||
| === Additional Features | === Additional Features | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user