parent
00f8991fac
commit
eeb0f56bac
|
@ -83,6 +83,7 @@ The following sections will go into more detail on the core components used by O
|
|||
** <<oauth2Client-auth-code-grant, Authorization Code>>
|
||||
** <<oauth2Client-refresh-token-grant, Refresh Token>>
|
||||
** <<oauth2Client-client-creds-grant, Client Credentials>>
|
||||
** <<oauth2Client-password-grant, Resource Owner Password Credentials>>
|
||||
* <<oauth2Client-additional-features>>
|
||||
** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>>
|
||||
|
||||
|
@ -781,6 +782,180 @@ public class OAuth2ClientController {
|
|||
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
||||
|
||||
|
||||
[[oauth2Client-password-grant]]
|
||||
==== Resource Owner Password Credentials
|
||||
|
||||
[NOTE]
|
||||
Please refer to the OAuth 2.0 Authorization Framework for further details on the https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials] grant.
|
||||
|
||||
|
||||
===== Requesting an Access Token
|
||||
|
||||
[NOTE]
|
||||
Please refer to the https://tools.ietf.org/html/rfc6749#section-4.3.2[Access Token Request/Response] protocol flow for the Resource Owner Password Credentials grant.
|
||||
|
||||
The default implementation of `OAuth2AccessTokenResponseClient` for the Resource Owner Password Credentials grant is `DefaultPasswordTokenResponseClient`, which uses a `RestOperations` when requesting an access token at the Authorization Server’s Token Endpoint.
|
||||
|
||||
The `DefaultPasswordTokenResponseClient` 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 `DefaultPasswordTokenResponseClient.setRequestEntityConverter()` with a custom `Converter<OAuth2PasswordGrantRequest, RequestEntity<?>>`.
|
||||
The default implementation `OAuth2PasswordGrantRequestEntityConverter` builds a `RequestEntity` representation of a standard https://tools.ietf.org/html/rfc6749#section-4.3.2[OAuth 2.0 Access Token Request].
|
||||
However, providing a custom `Converter`, would allow you to extend the standard Token Request and add custom parameter(s).
|
||||
|
||||
IMPORTANT: The custom `Converter` must return a valid `RequestEntity` representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.
|
||||
|
||||
|
||||
===== 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 `DefaultPasswordTokenResponseClient.setRestOperations()` with a custom configured `RestOperations`.
|
||||
The default `RestOperations` is configured as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
|
||||
new FormHttpMessageConverter(),
|
||||
new OAuth2AccessTokenResponseHttpMessageConverter()));
|
||||
|
||||
restTemplate.setErrorHandler(new 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 `DefaultPasswordTokenResponseClient` or provide your own implementation of `OAuth2AccessTokenResponseClient`, you'll need to configure it as shown in the following example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// Customize
|
||||
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
|
||||
|
||||
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
||||
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
|
||||
.refreshToken()
|
||||
.build();
|
||||
|
||||
...
|
||||
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
`OAuth2AuthorizedClientProviderBuilder.builder().password()` configures a `PasswordOAuth2AuthorizedClientProvider`,
|
||||
which is an implementation of an `OAuth2AuthorizedClientProvider` for the Resource Owner Password Credentials grant.
|
||||
|
||||
===== 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: password
|
||||
scope: read, write
|
||||
provider:
|
||||
okta:
|
||||
token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
|
||||
----
|
||||
|
||||
...and the `OAuth2AuthorizedClientManager` `@Bean`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public OAuth2AuthorizedClientManager authorizedClientManager(
|
||||
ClientRegistrationRepository clientRegistrationRepository,
|
||||
OAuth2AuthorizedClientRepository authorizedClientRepository) {
|
||||
|
||||
OAuth2AuthorizedClientProvider authorizedClientProvider =
|
||||
OAuth2AuthorizedClientProviderBuilder.builder()
|
||||
.password()
|
||||
.refreshToken()
|
||||
.build();
|
||||
|
||||
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
|
||||
new DefaultOAuth2AuthorizedClientManager(
|
||||
clientRegistrationRepository, authorizedClientRepository);
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
|
||||
// Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
|
||||
// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
|
||||
authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
|
||||
|
||||
return authorizedClientManager;
|
||||
}
|
||||
|
||||
private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper() {
|
||||
return authorizeRequest -> {
|
||||
Map<String, Object> contextAttributes = Collections.emptyMap();
|
||||
HttpServletRequest servletRequest = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
|
||||
String username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME);
|
||||
String password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD);
|
||||
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
|
||||
contextAttributes = new HashMap<>();
|
||||
|
||||
// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
|
||||
contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
|
||||
contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
|
||||
}
|
||||
return contextAttributes;
|
||||
};
|
||||
}
|
||||
----
|
||||
|
||||
You may obtain the `OAuth2AccessToken` as follows:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Controller
|
||||
public class OAuth2ClientController {
|
||||
|
||||
@Autowired
|
||||
private OAuth2AuthorizedClientManager authorizedClientManager;
|
||||
|
||||
@GetMapping("/")
|
||||
public String index(Authentication authentication,
|
||||
HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) {
|
||||
|
||||
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
|
||||
.principal(authentication)
|
||||
.attributes(attrs -> {
|
||||
attrs.put(HttpServletRequest.class.getName(), servletRequest);
|
||||
attrs.put(HttpServletResponse.class.getName(), servletResponse);
|
||||
})
|
||||
.build();
|
||||
OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
|
||||
|
||||
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
|
||||
|
||||
...
|
||||
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
`HttpServletRequest` and `HttpServletResponse` are both OPTIONAL attributes.
|
||||
If not provided, it will default to `ServletRequestAttributes` using `RequestContextHolder.getRequestAttributes()`.
|
||||
|
||||
|
||||
[[oauth2Client-additional-features]]
|
||||
=== Additional Features
|
||||
|
||||
|
|
Loading…
Reference in New Issue