This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Security 6.3.4!

Core Interfaces and Classes

This section describes the OAuth2 core interfaces and classes that Spring Security offers.

ClientRegistration

ClientRegistration is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.

A ClientRegistration object 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 is available only if the Spring Boot property spring.security.oauth2.client.provider.[providerId].issuerUri is configured.
14 (userInfoEndpoint)uri: The UserInfo Endpoint URI used to access the claims and 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.

You can initially configure a ClientRegistration by 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 follows:

  • Java

  • Kotlin

ClientRegistration clientRegistration =
    ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();
val clientRegistration = ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build()

As an alternative, you can use ClientRegistrations.fromOidcIssuerLocation() to query only 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 subset of the primary client registration information, which is stored with the Authorization Server.

Spring Boot 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:

  • Java

  • Kotlin

@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 (the 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 and 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 ability to look up an OAuth2AccessToken associated with a client so that it can be used to initiate a protected resource request.

The following listing shows an example:

  • Java

  • Kotlin

@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 auto-configuration registers an OAuth2AuthorizedClientRepository or an OAuth2AuthorizedClientService @Bean in the ApplicationContext. However, the application can override and register a custom OAuth2AuthorizedClientRepository or OAuth2AuthorizedClientService @Bean.

The default implementation of OAuth2AuthorizedClientService is InMemoryOAuth2AuthorizedClientService, which stores OAuth2AuthorizedClient objects in-memory.

Alternatively, you can configure the JDBC implementation JdbcOAuth2AuthorizedClientService to persist OAuth2AuthorizedClient instances in a database.

JdbcOAuth2AuthorizedClientService depends on the table definition described in OAuth 2.0 Client Schema.

OAuth2AuthorizedClientManager and 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, by using an OAuth2AuthorizedClientProvider.

  • Delegating the persistence of an OAuth2AuthorizedClient, typically by using an OAuth2AuthorizedClientService or OAuth2AuthorizedClientRepository.

  • 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 typically implement an authorization grant type, such as authorization_code, client_credentials, and others.

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. You can use OAuth2AuthorizedClientProviderBuilder 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:

  • Java

  • Kotlin

@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 delegates to the OAuth2AuthorizationSuccessHandler, which (by default) saves the OAuth2AuthorizedClient through the OAuth2AuthorizedClientRepository. In the case of a re-authorization failure (for example, a refresh token is no longer valid), the previously saved OAuth2AuthorizedClient is removed from the OAuth2AuthorizedClientRepository through the RemoveAuthorizedClientOAuth2AuthorizationFailureHandler. You can customize the default behavior through 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:

  • Java

  • Kotlin

@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:

  • Java

  • Kotlin

@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
}