There is a newer version available. Please update to Spring Security 5.6! |
OAuth 2.0 Client
The OAuth 2.0 Client features provide support for the Client role as defined in the OAuth 2.0 Authorization Framework.
At a high-level, the core features available are:
-
WebClient
integration for Servlet Environments (for requesting protected resources)
The HttpSecurity.oauth2Client()
DSL provides a number of configuration options for customizing the core components used by OAuth 2.0 Client.
In addition, HttpSecurity.oauth2Client().authorizationCodeGrant()
enables the customization of the Authorization Code grant.
The following code shows the complete configuration options provided by the HttpSecurity.oauth2Client()
DSL:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client(oauth2 -> oauth2
.clientRegistrationRepository(this.clientRegistrationRepository())
.authorizedClientRepository(this.authorizedClientRepository())
.authorizedClientService(this.authorizedClientService())
.authorizationCodeGrant(codeGrant -> codeGrant
.authorizationRequestRepository(this.authorizationRequestRepository())
.authorizationRequestResolver(this.authorizationRequestResolver())
.accessTokenResponseClient(this.accessTokenResponseClient())
)
);
}
}
@EnableWebSecurity
class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http {
oauth2Client {
clientRegistrationRepository = clientRegistrationRepository()
authorizedClientRepository = authorizedClientRepository()
authorizedClientService = authorizedClientService()
authorizationCodeGrant {
authorizationRequestRepository = authorizationRequestRepository()
authorizationRequestResolver = authorizationRequestResolver()
accessTokenResponseClient = accessTokenResponseClient()
}
}
}
}
}
In addition to the HttpSecurity.oauth2Client()
DSL, XML configuration is also supported.
The following code shows the complete configuration options available in the security namespace:
<http>
<oauth2-client client-registration-repository-ref="clientRegistrationRepository"
authorized-client-repository-ref="authorizedClientRepository"
authorized-client-service-ref="authorizedClientService">
<authorization-code-grant
authorization-request-repository-ref="authorizationRequestRepository"
authorization-request-resolver-ref="authorizationRequestResolver"
access-token-response-client-ref="accessTokenResponseClient"/>
</oauth2-client>
</http>
The OAuth2AuthorizedClientManager
is responsible for managing the authorization (or re-authorization) of an OAuth 2.0 Client, in collaboration with one or more OAuth2AuthorizedClientProvider
(s).
The following code shows an example of how to register an OAuth2AuthorizedClientManager
@Bean
and associate it with an OAuth2AuthorizedClientProvider
composite that provides support for the authorization_code
, refresh_token
, client_credentials
and password
authorization grant types:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
val authorizedClientProvider: OAuth2AuthorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build()
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
The following sections will go into more detail on the core components used by OAuth 2.0 Client and the configuration options available:
Core Interfaces / Classes
ClientRegistration
ClientRegistration
is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.
A client registration holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.
ClientRegistration
and its properties are defined as follows:
public final class ClientRegistration {
private String registrationId; (1)
private String clientId; (2)
private String clientSecret; (3)
private ClientAuthenticationMethod clientAuthenticationMethod; (4)
private AuthorizationGrantType authorizationGrantType; (5)
private String redirectUri; (6)
private Set<String> scopes; (7)
private ProviderDetails providerDetails;
private String clientName; (8)
public class ProviderDetails {
private String authorizationUri; (9)
private String tokenUri; (10)
private UserInfoEndpoint userInfoEndpoint;
private String jwkSetUri; (11)
private String issuerUri; (12)
private Map<String, Object> configurationMetadata; (13)
public class UserInfoEndpoint {
private String uri; (14)
private AuthenticationMethod authenticationMethod; (15)
private String userNameAttributeName; (16)
}
}
}
1 | registrationId : The ID that uniquely identifies the ClientRegistration . |
2 | clientId : The client identifier. |
3 | clientSecret : The client secret. |
4 | clientAuthenticationMethod : The method used to authenticate the Client with the Provider.
The supported values are client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none (public clients). |
5 | authorizationGrantType : The OAuth 2.0 Authorization Framework defines four Authorization Grant types.
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
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. |
8 | clientName : A descriptive name used for the client.
The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page. |
9 | authorizationUri : The Authorization Endpoint URI for the Authorization Server. |
10 | tokenUri : The Token Endpoint URI for the Authorization Server. |
11 | jwkSetUri : The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server,
which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response. |
12 | issuerUri : Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server. |
13 | configurationMetadata : The OpenID Provider Configuration Information.
This information will only be available if the Spring Boot 2.x property spring.security.oauth2.client.provider.[providerId].issuerUri is configured. |
14 | (userInfoEndpoint)uri : The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user. |
15 | (userInfoEndpoint)authenticationMethod : The authentication method used when sending the access token to the UserInfo Endpoint.
The supported values are header, form and query. |
16 | userNameAttributeName : The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user. |
A ClientRegistration
can be initially configured using discovery of an OpenID Connect Provider’s Configuration endpoint or an Authorization Server’s Metadata endpoint.
ClientRegistrations
provides convenience methods for configuring a ClientRegistration
in this way, as can be seen in the following example:
ClientRegistration clientRegistration =
ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()
The above code will query in series https://idp.example.com/issuer/.well-known/openid-configuration
, and then https://idp.example.com/.well-known/openid-configuration/issuer
, and finally https://idp.example.com/.well-known/oauth-authorization-server/issuer
, stopping at the first to return a 200 response.
As an alternative, you can use ClientRegistrations.fromOidcIssuerLocation()
to only query the OpenID Connect Provider’s Configuration endpoint.
ClientRegistrationRepository
The ClientRegistrationRepository
serves as a repository for OAuth 2.0 / OpenID Connect 1.0 ClientRegistration
(s).
Client registration information is ultimately stored and owned by the associated Authorization Server. This repository provides the ability to retrieve a sub-set of the primary client registration information, which is stored with the Authorization Server. |
Spring Boot 2.x auto-configuration binds each of the properties under spring.security.oauth2.client.registration.[registrationId]
to an instance of ClientRegistration
and then composes each of the ClientRegistration
instance(s) within a ClientRegistrationRepository
.
The default implementation of ClientRegistrationRepository is InMemoryClientRegistrationRepository .
|
The auto-configuration also registers the ClientRegistrationRepository
as a @Bean
in the ApplicationContext
so that it is available for dependency-injection, if needed by the application.
The following listing shows an example:
@Controller
public class OAuth2ClientController {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@GetMapping("/")
public String index() {
ClientRegistration oktaRegistration =
this.clientRegistrationRepository.findByRegistrationId("okta");
...
return "index";
}
}
@Controller
class OAuth2ClientController {
@Autowired
private lateinit var clientRegistrationRepository: ClientRegistrationRepository
@GetMapping("/")
fun index(): String {
val oktaRegistration =
this.clientRegistrationRepository.findByRegistrationId("okta")
//...
return "index";
}
}
OAuth2AuthorizedClient
OAuth2AuthorizedClient
is a representation of an Authorized Client.
A client is considered to be authorized when the end-user (Resource Owner) has granted authorization to the client to access its protected resources.
OAuth2AuthorizedClient
serves the purpose of associating an OAuth2AccessToken
(and optional OAuth2RefreshToken
) to a ClientRegistration
(client) and resource owner, who is the Principal
end-user that granted the authorization.
OAuth2AuthorizedClientRepository / OAuth2AuthorizedClientService
OAuth2AuthorizedClientRepository
is responsible for persisting OAuth2AuthorizedClient
(s) between web requests.
Whereas, the primary role of OAuth2AuthorizedClientService
is to manage OAuth2AuthorizedClient
(s) at the application-level.
From a developer perspective, the OAuth2AuthorizedClientRepository
or OAuth2AuthorizedClientService
provides the capability to lookup an OAuth2AccessToken
associated with a client so that it may be used to initiate a protected resource request.
The following listing shows an example:
@Controller
public class OAuth2ClientController {
@Autowired
private OAuth2AuthorizedClientService authorizedClientService;
@GetMapping("/")
public String index(Authentication authentication) {
OAuth2AuthorizedClient authorizedClient =
this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName());
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
...
return "index";
}
}
@Controller
class OAuth2ClientController {
@Autowired
private lateinit var authorizedClientService: OAuth2AuthorizedClientService
@GetMapping("/")
fun index(authentication: Authentication): String {
val authorizedClient: OAuth2AuthorizedClient =
this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName());
val accessToken = authorizedClient.accessToken
...
return "index";
}
}
Spring Boot 2.x auto-configuration registers an OAuth2AuthorizedClientRepository and/or OAuth2AuthorizedClientService @Bean in the ApplicationContext .
However, the application may choose to override and register a custom OAuth2AuthorizedClientRepository or OAuth2AuthorizedClientService @Bean .
|
The default implementation of OAuth2AuthorizedClientService
is InMemoryOAuth2AuthorizedClientService
, which stores OAuth2AuthorizedClient
(s) in-memory.
Alternatively, the JDBC implementation JdbcOAuth2AuthorizedClientService
may be configured for persisting OAuth2AuthorizedClient
(s) in a database.
JdbcOAuth2AuthorizedClientService depends on the table definition described in OAuth 2.0 Client Schema.
|
OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider
The OAuth2AuthorizedClientManager
is responsible for the overall management of OAuth2AuthorizedClient
(s).
The primary responsibilities include:
-
Authorizing (or re-authorizing) an OAuth 2.0 Client, using an
OAuth2AuthorizedClientProvider
. -
Delegating the persistence of an
OAuth2AuthorizedClient
, typically using anOAuth2AuthorizedClientService
orOAuth2AuthorizedClientRepository
. -
Delegating to an
OAuth2AuthorizationSuccessHandler
when an OAuth 2.0 Client has been successfully authorized (or re-authorized). -
Delegating to an
OAuth2AuthorizationFailureHandler
when an OAuth 2.0 Client fails to authorize (or re-authorize).
An OAuth2AuthorizedClientProvider
implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client.
Implementations will typically implement an authorization grant type, eg. authorization_code
, client_credentials
, etc.
The default implementation of OAuth2AuthorizedClientManager
is DefaultOAuth2AuthorizedClientManager
, which is associated with an OAuth2AuthorizedClientProvider
that may support multiple authorization grant types using a delegation-based composite.
The OAuth2AuthorizedClientProviderBuilder
may be used to configure and build the delegation-based composite.
The following code shows an example of how to configure and build an OAuth2AuthorizedClientProvider
composite that provides support for the authorization_code
, refresh_token
, client_credentials
and password
authorization grant types:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build()
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
When an authorization attempt succeeds, the DefaultOAuth2AuthorizedClientManager
will delegate to the OAuth2AuthorizationSuccessHandler
, which (by default) will save the OAuth2AuthorizedClient
via the OAuth2AuthorizedClientRepository
.
In the case of a re-authorization failure, eg. a refresh token is no longer valid, the previously saved OAuth2AuthorizedClient
will be removed from the OAuth2AuthorizedClientRepository
via the RemoveAuthorizedClientOAuth2AuthorizationFailureHandler
.
The default behaviour may be customized via setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)
and setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler)
.
The DefaultOAuth2AuthorizedClientManager
is also associated with a contextAttributesMapper
of type Function<OAuth2AuthorizeRequest, Map<String, Object>>
, which is responsible for mapping attribute(s) from the OAuth2AuthorizeRequest
to a Map
of attributes to be associated to the OAuth2AuthorizationContext
.
This can be useful when you need to supply an OAuth2AuthorizedClientProvider
with required (supported) attribute(s), eg. the PasswordOAuth2AuthorizedClientProvider
requires the resource owner’s username
and password
to be available in OAuth2AuthorizationContext.getAttributes()
.
The following code shows an example of the contextAttributesMapper
:
@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;
};
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.refreshToken()
.build()
val authorizedClientManager = 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 fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
return Function { authorizeRequest ->
var contextAttributes: MutableMap<String, Any> = mutableMapOf()
val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
val username: String = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
val password: String = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
contextAttributes = hashMapOf()
// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
}
contextAttributes
}
}
The DefaultOAuth2AuthorizedClientManager
is designed to be used within the context of a HttpServletRequest
.
When operating outside of a HttpServletRequest
context, use AuthorizedClientServiceOAuth2AuthorizedClientManager
instead.
A service application is a common use case for when to use an AuthorizedClientServiceOAuth2AuthorizedClientManager
.
Service applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account.
An OAuth 2.0 Client configured with the client_credentials
grant type can be considered a type of service application.
The following code shows an example of how to configure an AuthorizedClientServiceOAuth2AuthorizedClientManager
that provides support for the client_credentials
grant type:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientService: OAuth2AuthorizedClientService): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build()
val authorizedClientManager = AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
Authorization Grant Support
Authorization Code
Please refer to the OAuth 2.0 Authorization Framework for further details on the Authorization Code grant. |
Obtaining Authorization
Please refer to the Authorization Request/Response protocol flow for the Authorization Code grant. |
Initiating the Authorization Request
The OAuth2AuthorizationRequestRedirectFilter
uses an OAuth2AuthorizationRequestResolver
to resolve an OAuth2AuthorizationRequest
and initiate the Authorization Code grant flow by redirecting the end-user’s user-agent to the Authorization Server’s Authorization Endpoint.
The primary role of the OAuth2AuthorizationRequestResolver
is to resolve an OAuth2AuthorizationRequest
from the provided web request.
The default implementation DefaultOAuth2AuthorizationRequestResolver
matches on the (default) path /oauth2/authorization/{registrationId}
extracting the registrationId
and using it to build the OAuth2AuthorizationRequest
for the associated ClientRegistration
.
Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/authorized/okta"
scope: read, write
provider:
okta:
authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
A request with the base path /oauth2/authorization/okta
will initiate the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter
and ultimately start the Authorization Code grant flow.
The AuthorizationCodeOAuth2AuthorizedClientProvider is an implementation of OAuth2AuthorizedClientProvider for the Authorization Code grant,
which also initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter .
|
If the OAuth 2.0 Client is a Public Client, then configure the OAuth 2.0 Client registration as follows:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-authentication-method: none
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/authorized/okta"
...
Public Clients are supported using Proof Key for Code Exchange (PKCE). If the client is running in an untrusted environment (eg. native application or web browser-based application) and therefore incapable of maintaining the confidentiality of it’s credentials, PKCE will automatically be used when the following conditions are true:
-
client-secret
is omitted (or empty) -
client-authentication-method
is set to "none" (ClientAuthenticationMethod.NONE
)
The DefaultOAuth2AuthorizationRequestResolver
also supports URI
template variables for the redirect-uri
using UriComponentsBuilder
.
The following configuration uses all the supported URI
template variables:
spring:
security:
oauth2:
client:
registration:
okta:
...
redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
...
{baseUrl} resolves to {baseScheme}://{baseHost}{basePort}{basePath}
|
Configuring the redirect-uri
with URI
template variables is especially useful when the OAuth 2.0 Client is running behind a Proxy Server.
This ensures that the X-Forwarded-*
headers are used when expanding the redirect-uri
.
Customizing the Authorization Request
One of the primary use cases an OAuth2AuthorizationRequestResolver
can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.
For example, OpenID Connect defines additional OAuth 2.0 request parameters for the Authorization Code Flow extending from the standard parameters defined in the OAuth 2.0 Authorization Framework.
One of those extended parameters is the prompt
parameter.
OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are: none, login, consent, select_account |
The following example shows how to configure the DefaultOAuth2AuthorizationRequestResolver
with a Consumer<OAuth2AuthorizationRequest.Builder>
that customizes the Authorization Request for oauth2Login()
, by including the request parameter prompt=consent
.
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.authorizationEndpoint(authorization -> authorization
.authorizationRequestResolver(
authorizationRequestResolver(this.clientRegistrationRepository)
)
)
);
}
private OAuth2AuthorizationRequestResolver authorizationRequestResolver(
ClientRegistrationRepository clientRegistrationRepository) {
DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
new DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository, "/oauth2/authorization");
authorizationRequestResolver.setAuthorizationRequestCustomizer(
authorizationRequestCustomizer());
return authorizationRequestResolver;
}
private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
return customizer -> customizer
.additionalParameters(params -> params.put("prompt", "consent"));
}
}
@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
@Autowired
private lateinit var customClientRegistrationRepository: ClientRegistrationRepository
override fun configure(http: HttpSecurity) {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2Login {
authorizationEndpoint {
authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
}
}
}
}
private fun authorizationRequestResolver(
clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver? {
val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
clientRegistrationRepository, "/oauth2/authorization")
authorizationRequestResolver.setAuthorizationRequestCustomizer(
authorizationRequestCustomizer())
return authorizationRequestResolver
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
return Consumer { customizer ->
customizer
.additionalParameters { params -> params["prompt"] = "consent" }
}
}
}
For the simple use case, where the additional request parameter is always the same for a specific provider, it may be added directly in the authorization-uri
property.
For example, if the value for the request parameter prompt
is always consent
for the provider okta
, than simply configure as follows:
spring:
security:
oauth2:
client:
provider:
okta:
authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent
The preceding example shows the common use case of adding a custom parameter on top of the standard parameters.
Alternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by simply overriding the OAuth2AuthorizationRequest.authorizationRequestUri
property.
OAuth2AuthorizationRequest.Builder.build() constructs the OAuth2AuthorizationRequest.authorizationRequestUri , which represents the Authorization Request URI including all query parameters using the application/x-www-form-urlencoded format.
|
The following example shows a variation of authorizationRequestCustomizer()
from the preceding example, and instead overrides the OAuth2AuthorizationRequest.authorizationRequestUri
property.
private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
return customizer -> customizer
.authorizationRequestUri(uriBuilder -> uriBuilder
.queryParam("prompt", "consent").build());
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
customizer
.authorizationRequestUri { uriBuilder: UriBuilder ->
uriBuilder
.queryParam("prompt", "consent").build()
}
}
}
Storing the Authorization Request
The AuthorizationRequestRepository
is responsible for the persistence of the OAuth2AuthorizationRequest
from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).
The OAuth2AuthorizationRequest is used to correlate and validate the Authorization Response.
|
The default implementation of AuthorizationRequestRepository
is HttpSessionOAuth2AuthorizationRequestRepository
, which stores the OAuth2AuthorizationRequest
in the HttpSession
.
If you have a custom implementation of AuthorizationRequestRepository
, you may configure it as shown in the following example:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client(oauth2 -> oauth2
.authorizationCodeGrant(codeGrant -> codeGrant
.authorizationRequestRepository(this.authorizationRequestRepository())
...
)
);
}
}
@EnableWebSecurity
class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http {
oauth2Client {
authorizationCodeGrant {
authorizationRequestRepository = authorizationRequestRepository()
}
}
}
}
}
<http>
<oauth2-client>
<authorization-code-grant authorization-request-repository-ref="authorizationRequestRepository"/>
</oauth2-client>
</http>
Requesting an Access Token
Please refer to the Access Token Request/Response protocol flow for the Authorization Code grant. |
The default implementation of OAuth2AccessTokenResponseClient
for the Authorization Code grant is DefaultAuthorizationCodeTokenResponseClient
, which uses a RestOperations
for exchanging an authorization code for an access token at the Authorization Server’s Token Endpoint.
The DefaultAuthorizationCodeTokenResponseClient
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 DefaultAuthorizationCodeTokenResponseClient.setRequestEntityConverter()
with a custom Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>>
.
The default implementation OAuth2AuthorizationCodeGrantRequestEntityConverter
builds a RequestEntity
representation of a standard 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).
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 DefaultAuthorizationCodeTokenResponseClient.setRestOperations()
with a custom configured RestOperations
.
The default RestOperations
is configured as follows:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
FormHttpMessageConverter(),
OAuth2AccessTokenResponseHttpMessageConverter()))
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
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 DefaultAuthorizationCodeTokenResponseClient
or provide your own implementation of OAuth2AccessTokenResponseClient
, you’ll need to configure it as shown in the following example:
@EnableWebSecurity
public class OAuth2ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.oauth2Client(oauth2 -> oauth2
.authorizationCodeGrant(codeGrant -> codeGrant
.accessTokenResponseClient(this.accessTokenResponseClient())
...
)
);
}
}
@EnableWebSecurity
class OAuth2ClientSecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http {
oauth2Client {
authorizationCodeGrant {
accessTokenResponseClient = accessTokenResponseClient()
}
}
}
}
}
<http>
<oauth2-client>
<authorization-code-grant access-token-response-client-ref="accessTokenResponseClient"/>
</oauth2-client>
</http>
Refresh Token
Please refer to the OAuth 2.0 Authorization Framework for further details on the Refresh Token. |
Refreshing an Access Token
Please refer to the Access Token Request/Response protocol flow for the Refresh Token grant. |
The default implementation of OAuth2AccessTokenResponseClient
for the Refresh Token grant is DefaultRefreshTokenTokenResponseClient
, which uses a RestOperations
when refreshing an access token at the Authorization Server’s Token Endpoint.
The DefaultRefreshTokenTokenResponseClient
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 DefaultRefreshTokenTokenResponseClient.setRequestEntityConverter()
with a custom Converter<OAuth2RefreshTokenGrantRequest, RequestEntity<?>>
.
The default implementation OAuth2RefreshTokenGrantRequestEntityConverter
builds a RequestEntity
representation of a standard 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).
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 DefaultRefreshTokenTokenResponseClient.setRestOperations()
with a custom configured RestOperations
.
The default RestOperations
is configured as follows:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
FormHttpMessageConverter(),
OAuth2AccessTokenResponseHttpMessageConverter()))
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
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 DefaultRefreshTokenTokenResponseClient
or provide your own implementation of OAuth2AccessTokenResponseClient
, you’ll need to configure it as shown in the following example:
// Customize
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
.build();
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
.build()
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
OAuth2AuthorizedClientProviderBuilder.builder().refreshToken() configures a RefreshTokenOAuth2AuthorizedClientProvider ,
which is an implementation of an OAuth2AuthorizedClientProvider for the Refresh Token grant.
|
The OAuth2RefreshToken
may optionally be returned in the Access Token Response for the authorization_code
and password
grant types.
If the OAuth2AuthorizedClient.getRefreshToken()
is available and the OAuth2AuthorizedClient.getAccessToken()
is expired, it will automatically be refreshed by the RefreshTokenOAuth2AuthorizedClientProvider
.
Client Credentials
Please refer to the OAuth 2.0 Authorization Framework for further details on the Client Credentials grant. |
Requesting an Access Token
Please refer to the Access Token Request/Response protocol flow for the Client Credentials grant. |
The default implementation of OAuth2AccessTokenResponseClient
for the Client Credentials grant is DefaultClientCredentialsTokenResponseClient
, which uses a RestOperations
when requesting an access token at the Authorization Server’s Token Endpoint.
The DefaultClientCredentialsTokenResponseClient
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 DefaultClientCredentialsTokenResponseClient.setRequestEntityConverter()
with a custom Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>>
.
The default implementation OAuth2ClientCredentialsGrantRequestEntityConverter
builds a RequestEntity
representation of a standard 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).
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 DefaultClientCredentialsTokenResponseClient.setRestOperations()
with a custom configured RestOperations
.
The default RestOperations
is configured as follows:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
FormHttpMessageConverter(),
OAuth2AccessTokenResponseHttpMessageConverter()))
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
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 DefaultClientCredentialsTokenResponseClient
or provide your own implementation of OAuth2AccessTokenResponseClient
, you’ll need to configure it as shown in the following example:
// Customize
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
.build();
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
.build()
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() configures a ClientCredentialsOAuth2AuthorizedClientProvider ,
which is an implementation of an OAuth2AuthorizedClientProvider for the Client Credentials grant.
|
Using the Access Token
Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
authorization-grant-type: client_credentials
scope: read, write
provider:
okta:
token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token
…and the OAuth2AuthorizedClientManager
@Bean
:
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build()
val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository)
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
return authorizedClientManager
}
You may obtain the OAuth2AccessToken
as follows:
@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";
}
}
class OAuth2ClientController {
@Autowired
private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager
@GetMapping("/")
fun index(authentication: Authentication?,
servletRequest: HttpServletRequest,
servletResponse: HttpServletResponse): String {
val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
.principal(authentication)
.attributes(Consumer { attrs: MutableMap<String, Any> ->
attrs[HttpServletRequest::class.java.name] = servletRequest
attrs[HttpServletResponse::class.java.name] = servletResponse
})
.build()
val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
val accessToken: OAuth2AccessToken = authorizedClient.accessToken
...
return "index"
}
}
HttpServletRequest and HttpServletResponse are both OPTIONAL attributes.
If not provided, it will default to ServletRequestAttributes using RequestContextHolder.getRequestAttributes() .
|
Resource Owner Password Credentials
Please refer to the OAuth 2.0 Authorization Framework for further details on the Resource Owner Password Credentials grant. |
Requesting an Access Token
Please refer to the 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 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).
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:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
FormHttpMessageConverter(),
OAuth2AccessTokenResponseHttpMessageConverter()))
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
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:
// Customize
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
.refreshToken()
.build();
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
val passwordTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password { it.accessTokenResponseClient(passwordTokenResponseClient) }
.refreshToken()
.build()
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
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:
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
:
@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;
};
}
@Bean
fun authorizedClientManager(
clientRegistrationRepository: ClientRegistrationRepository,
authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.password()
.refreshToken()
.build()
val authorizedClientManager = 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 fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
return Function { authorizeRequest ->
var contextAttributes: MutableMap<String, Any> = mutableMapOf()
val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
val username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
val password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
contextAttributes = hashMapOf()
// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
}
contextAttributes
}
}
You may obtain the OAuth2AccessToken
as follows:
@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";
}
}
@Controller
class OAuth2ClientController {
@Autowired
private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager
@GetMapping("/")
fun index(authentication: Authentication?,
servletRequest: HttpServletRequest,
servletResponse: HttpServletResponse): String {
val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
.principal(authentication)
.attributes(Consumer {
it[HttpServletRequest::class.java.name] = servletRequest
it[HttpServletResponse::class.java.name] = servletResponse
})
.build()
val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
val accessToken: OAuth2AccessToken = authorizedClient.accessToken
...
return "index"
}
}
HttpServletRequest and HttpServletResponse are both OPTIONAL attributes.
If not provided, it will default to ServletRequestAttributes using RequestContextHolder.getRequestAttributes() .
|
JWT Bearer
Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the JWT Bearer grant. |
Requesting an Access Token
Please refer to the 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 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:
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
new OAuth2AccessTokenResponseHttpMessageConverter()));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
FormHttpMessageConverter(),
OAuth2AccessTokenResponseHttpMessageConverter()))
restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()
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:
// Customize
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...
JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.provider(jwtBearerAuthorizedClientProvider)
.build();
...
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// 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:
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
:
@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;
}
@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:
@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();
...
}
}
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
...
}
}
Client Authentication Support
JWT Bearer
Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on JWT Bearer Client Authentication. |
The default implementation for JWT Bearer Client Authentication is NimbusJwtClientAuthenticationParametersConverter
,
which is a Converter
that customizes the Token Request parameters by adding
a signed JSON Web Token (JWS) in the client_assertion
parameter.
The java.security.PrivateKey
or javax.crypto.SecretKey
used for signing the JWS
is supplied by the com.nimbusds.jose.jwk.JWK
resolver associated with NimbusJwtClientAuthenticationParametersConverter
.
Authenticate using private_key_jwt
Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-authentication-method: private_key_jwt
authorization-grant-type: authorization_code
...
The following example shows how to configure DefaultAuthorizationCodeTokenResponseClient
:
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
// Assuming RSA key type
RSAPublicKey publicKey = ...
RSAPrivateKey privateKey = ...
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
return null;
};
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
val jwkResolver: Function<ClientRegistration, JWK> =
Function<ClientRegistration, JWK> { clientRegistration ->
if (clientRegistration.clientAuthenticationMethod.equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
// Assuming RSA key type
var publicKey: RSAPublicKey
var privateKey: RSAPrivateKey
RSAKey.Builder(publicKey) = //...
.privateKey(privateKey) = //...
.keyID(UUID.randomUUID().toString())
.build()
}
null
}
val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter()
requestEntityConverter.addParametersConverter(
NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)
val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient()
tokenResponseClient.setRequestEntityConverter(requestEntityConverter)
Authenticate using client_secret_jwt
Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
client-authentication-method: client_secret_jwt
authorization-grant-type: client_credentials
...
The following example shows how to configure DefaultClientCredentialsTokenResponseClient
:
Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
SecretKeySpec secretKey = new SecretKeySpec(
clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
"HmacSHA256");
return new OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build();
}
return null;
};
OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));
DefaultClientCredentialsTokenResponseClient tokenResponseClient =
new DefaultClientCredentialsTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
val jwkResolver = Function<ClientRegistration, JWK?> { clientRegistration: ClientRegistration ->
if (clientRegistration.clientAuthenticationMethod == ClientAuthenticationMethod.CLIENT_SECRET_JWT) {
val secretKey = SecretKeySpec(
clientRegistration.clientSecret.toByteArray(StandardCharsets.UTF_8),
"HmacSHA256"
)
OctetSequenceKey.Builder(secretKey)
.keyID(UUID.randomUUID().toString())
.build()
}
null
}
val requestEntityConverter = OAuth2ClientCredentialsGrantRequestEntityConverter()
requestEntityConverter.addParametersConverter(
NimbusJwtClientAuthenticationParametersConverter(jwkResolver)
)
val tokenResponseClient = DefaultClientCredentialsTokenResponseClient()
tokenResponseClient.setRequestEntityConverter(requestEntityConverter)
Additional Features
Resolving an Authorized Client
The @RegisteredOAuth2AuthorizedClient
annotation provides the capability of resolving a method parameter to an argument value of type OAuth2AuthorizedClient
.
This is a convenient alternative compared to accessing the OAuth2AuthorizedClient
using the OAuth2AuthorizedClientManager
or OAuth2AuthorizedClientService
.
@Controller
public class OAuth2ClientController {
@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
...
return "index";
}
}
@Controller
class OAuth2ClientController {
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
val accessToken = authorizedClient.accessToken
...
return "index"
}
}
The @RegisteredOAuth2AuthorizedClient
annotation is handled by OAuth2AuthorizedClientArgumentResolver
, which directly uses an OAuth2AuthorizedClientManager and therefore inherits it’s capabilities.
WebClient integration for Servlet Environments
The OAuth 2.0 Client support integrates with WebClient
using an ExchangeFilterFunction
.
The ServletOAuth2AuthorizedClientExchangeFilterFunction
provides a simple mechanism for requesting protected resources by using an OAuth2AuthorizedClient
and including the associated OAuth2AccessToken
as a Bearer Token.
It directly uses an OAuth2AuthorizedClientManager and therefore inherits the following capabilities:
-
An
OAuth2AccessToken
will be requested if the client has not yet been authorized.-
authorization_code
- triggers the Authorization Request redirect to initiate the flow -
client_credentials
- the access token is obtained directly from the Token Endpoint -
password
- the access token is obtained directly from the Token Endpoint
-
-
If the
OAuth2AccessToken
is expired, it will be refreshed (or renewed) if anOAuth2AuthorizedClientProvider
is available to perform the authorization
The following code shows an example of how to configure WebClient
with OAuth 2.0 Client support:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build()
}
Providing the Authorized Client
The ServletOAuth2AuthorizedClientExchangeFilterFunction
determines the client to use (for a request) by resolving the OAuth2AuthorizedClient
from the ClientRequest.attributes()
(request attributes).
The following code shows how to set an OAuth2AuthorizedClient
as a request attribute:
@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...
String body = webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono(String.class)
.block();
...
return "index";
}
@GetMapping("/")
fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String {
val resourceUri: String = ...
val body: String = webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) (1)
.retrieve()
.bodyToMono()
.block()
...
return "index"
}
1 | oauth2AuthorizedClient() is a static method in ServletOAuth2AuthorizedClientExchangeFilterFunction . |
The following code shows how to set the ClientRegistration.getRegistrationId()
as a request attribute:
@GetMapping("/")
public String index() {
String resourceUri = ...
String body = webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono(String.class)
.block();
...
return "index";
}
@GetMapping("/")
fun index(): String {
val resourceUri: String = ...
val body: String = webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) (1)
.retrieve()
.bodyToMono()
.block()
...
return "index"
}
1 | clientRegistrationId() is a static method in ServletOAuth2AuthorizedClientExchangeFilterFunction . |
Defaulting the Authorized Client
If neither OAuth2AuthorizedClient
or ClientRegistration.getRegistrationId()
is provided as a request attribute, the ServletOAuth2AuthorizedClientExchangeFilterFunction
can determine the default client to use depending on it’s configuration.
If setDefaultOAuth2AuthorizedClient(true)
is configured and the user has authenticated using HttpSecurity.oauth2Login()
, the OAuth2AccessToken
associated with the current OAuth2AuthenticationToken
is used.
The following code shows the specific configuration:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultOAuth2AuthorizedClient(true)
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build()
}
It is recommended to be cautious with this feature since all HTTP requests will receive the access token. |
Alternatively, if setDefaultClientRegistrationId("okta")
is configured with a valid ClientRegistration
, the OAuth2AccessToken
associated with the OAuth2AuthorizedClient
is used.
The following code shows the specific configuration:
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("okta");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
@Bean
fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient {
val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth2Client.setDefaultClientRegistrationId("okta")
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build()
}
It is recommended to be cautious with this feature since all HTTP requests will receive the access token. |