This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Authorization Server 1.4.0! |
Configuration Model
Default configuration
OAuth2AuthorizationServerConfiguration
is a @Configuration
that provides the minimal default configuration for an OAuth2 authorization server.
OAuth2AuthorizationServerConfiguration
uses OAuth2AuthorizationServerConfigurer
to apply the default configuration and registers a SecurityFilterChain
@Bean
composed of all the infrastructure components supporting an OAuth2 authorization server.
The OAuth2 authorization server SecurityFilterChain
@Bean
is configured with the following default protocol endpoints:
The JWK Set endpoint is configured only if a JWKSource<SecurityContext> @Bean is registered.
|
The following example shows how to use OAuth2AuthorizationServerConfiguration
to apply the minimal default configuration:
@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {
@Bean
public RegisteredClientRepository registeredClientRepository() {
List<RegisteredClient> registrations = ...
return new InMemoryRegisteredClientRepository(registrations);
}
@Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = ...
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
}
The authorization_code grant requires the resource owner to be authenticated. Therefore, a user authentication mechanism must be configured in addition to the default OAuth2 security configuration. |
OpenID Connect 1.0 is disabled in the default configuration. The following example shows how to enable OpenID Connect 1.0 by initializing the OidcConfigurer
:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.oidc(Customizer.withDefaults()) // Initialize `OidcConfigurer`
);
return http.build();
}
In addition to the default protocol endpoints, the OAuth2 authorization server SecurityFilterChain
@Bean
is configured with the following OpenID Connect 1.0 protocol endpoints:
The OpenID Connect 1.0 Client Registration endpoint is disabled by default because many deployments do not require dynamic client registration. |
OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource<SecurityContext>) is a convenience (static ) utility method that can be used to register a JwtDecoder @Bean , which is REQUIRED for the OpenID Connect 1.0 UserInfo endpoint and the OpenID Connect 1.0 Client Registration endpoint.
|
The following example shows how to register a JwtDecoder
@Bean
:
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
The main intent of OAuth2AuthorizationServerConfiguration
is to provide a convenient method to apply the minimal default configuration for an OAuth2 authorization server. However, in most cases, customizing the configuration will be required.
Customizing the configuration
OAuth2AuthorizationServerConfigurer
provides the ability to fully customize the security configuration for an OAuth2 authorization server.
It lets you specify the core components to use - for example, RegisteredClientRepository
, OAuth2AuthorizationService
, OAuth2TokenGenerator
, and others.
Furthermore, it lets you customize the request processing logic for the protocol endpoints – for example, authorization endpoint, device authorization endpoint, device verification endpoint, token endpoint, token introspection endpoint, and others.
OAuth2AuthorizationServerConfigurer
provides the following configuration options:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.registeredClientRepository(registeredClientRepository) (1)
.authorizationService(authorizationService) (2)
.authorizationConsentService(authorizationConsentService) (3)
.authorizationServerSettings(authorizationServerSettings) (4)
.tokenGenerator(tokenGenerator) (5)
.clientAuthentication(clientAuthentication -> { }) (6)
.authorizationEndpoint(authorizationEndpoint -> { }) (7)
.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { }) (8)
.deviceVerificationEndpoint(deviceVerificationEndpoint -> { }) (9)
.tokenEndpoint(tokenEndpoint -> { }) (10)
.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { }) (11)
.tokenRevocationEndpoint(tokenRevocationEndpoint -> { }) (12)
.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { }) (13)
.oidc(oidc -> oidc
.providerConfigurationEndpoint(providerConfigurationEndpoint -> { }) (14)
.logoutEndpoint(logoutEndpoint -> { }) (15)
.userInfoEndpoint(userInfoEndpoint -> { }) (16)
.clientRegistrationEndpoint(clientRegistrationEndpoint -> { }) (17)
)
);
return http.build();
}
1 | registeredClientRepository() : The RegisteredClientRepository (REQUIRED) for managing new and existing clients. |
2 | authorizationService() : The OAuth2AuthorizationService for managing new and existing authorizations. |
3 | authorizationConsentService() : The OAuth2AuthorizationConsentService for managing new and existing authorization consents. |
4 | authorizationServerSettings() : The AuthorizationServerSettings (REQUIRED) for customizing configuration settings for the OAuth2 authorization server. |
5 | tokenGenerator() : The OAuth2TokenGenerator for generating tokens supported by the OAuth2 authorization server. |
6 | clientAuthentication() : The configurer for OAuth2 Client Authentication. |
7 | authorizationEndpoint() : The configurer for the OAuth2 Authorization endpoint. |
8 | deviceAuthorizationEndpoint() : The configurer for the OAuth2 Device Authorization endpoint. |
9 | deviceVerificationEndpoint() : The configurer for the OAuth2 Device Verification endpoint. |
10 | tokenEndpoint() : The configurer for the OAuth2 Token endpoint. |
11 | tokenIntrospectionEndpoint() : The configurer for the OAuth2 Token Introspection endpoint. |
12 | tokenRevocationEndpoint() : The configurer for the OAuth2 Token Revocation endpoint. |
13 | authorizationServerMetadataEndpoint() : The configurer for the OAuth2 Authorization Server Metadata endpoint. |
14 | providerConfigurationEndpoint() : The configurer for the OpenID Connect 1.0 Provider Configuration endpoint. |
15 | logoutEndpoint() : The configurer for the OpenID Connect 1.0 Logout endpoint. |
16 | userInfoEndpoint() : The configurer for the OpenID Connect 1.0 UserInfo endpoint. |
17 | clientRegistrationEndpoint() : The configurer for the OpenID Connect 1.0 Client Registration endpoint. |
Configuring Authorization Server Settings
AuthorizationServerSettings
contains the configuration settings for the OAuth2 authorization server.
It specifies the URI
for the protocol endpoints as well as the issuer identifier.
The default URI
for the protocol endpoints are as follows:
public final class AuthorizationServerSettings extends AbstractSettings {
...
public static Builder builder() {
return new Builder()
.authorizationEndpoint("/oauth2/authorize")
.deviceAuthorizationEndpoint("/oauth2/device_authorization")
.deviceVerificationEndpoint("/oauth2/device_verification")
.tokenEndpoint("/oauth2/token")
.tokenIntrospectionEndpoint("/oauth2/introspect")
.tokenRevocationEndpoint("/oauth2/revoke")
.jwkSetEndpoint("/oauth2/jwks")
.oidcLogoutEndpoint("/connect/logout")
.oidcUserInfoEndpoint("/userinfo")
.oidcClientRegistrationEndpoint("/connect/register");
}
...
}
AuthorizationServerSettings is a REQUIRED component.
|
@Import(OAuth2AuthorizationServerConfiguration.class) automatically registers an AuthorizationServerSettings @Bean , if not already provided.
|
The following example shows how to customize the configuration settings and register an AuthorizationServerSettings
@Bean
:
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("https://example.com")
.authorizationEndpoint("/oauth2/v1/authorize")
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
.tokenEndpoint("/oauth2/v1/token")
.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
.tokenRevocationEndpoint("/oauth2/v1/revoke")
.jwkSetEndpoint("/oauth2/v1/jwks")
.oidcLogoutEndpoint("/connect/v1/logout")
.oidcUserInfoEndpoint("/connect/v1/userinfo")
.oidcClientRegistrationEndpoint("/connect/v1/register")
.build();
}
The AuthorizationServerContext
is a context object that holds information of the Authorization Server runtime environment.
It provides access to the AuthorizationServerSettings
and the “current” issuer identifier.
If the issuer identifier is not configured in AuthorizationServerSettings.builder().issuer(String) , it is resolved from the current request.
|
The AuthorizationServerContext is accessible through the AuthorizationServerContextHolder , which associates it with the current request thread by using a ThreadLocal .
|
Configuring Client Authentication
OAuth2ClientAuthenticationConfigurer
provides the ability to customize OAuth2 client authentication.
It defines extension points that let you customize the pre-processing, main processing, and post-processing logic for client authentication requests.
OAuth2ClientAuthenticationConfigurer
provides the following configuration options:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.clientAuthentication(clientAuthentication ->
clientAuthentication
.authenticationConverter(authenticationConverter) (1)
.authenticationConverters(authenticationConvertersConsumer) (2)
.authenticationProvider(authenticationProvider) (3)
.authenticationProviders(authenticationProvidersConsumer) (4)
.authenticationSuccessHandler(authenticationSuccessHandler) (5)
.errorResponseHandler(errorResponseHandler) (6)
)
);
return http.build();
}
1 | authenticationConverter() : Adds an AuthenticationConverter (pre-processor) used when attempting to extract client credentials from HttpServletRequest to an instance of OAuth2ClientAuthenticationToken . |
2 | authenticationConverters() : Sets the Consumer providing access to the List of default and (optionally) added AuthenticationConverter 's allowing the ability to add, remove, or customize a specific AuthenticationConverter . |
3 | authenticationProvider() : Adds an AuthenticationProvider (main processor) used for authenticating the OAuth2ClientAuthenticationToken . |
4 | authenticationProviders() : Sets the Consumer providing access to the List of default and (optionally) added AuthenticationProvider 's allowing the ability to add, remove, or customize a specific AuthenticationProvider . |
5 | authenticationSuccessHandler() : The AuthenticationSuccessHandler (post-processor) used for handling a successful client authentication and associating the OAuth2ClientAuthenticationToken to the SecurityContext . |
6 | errorResponseHandler() : The AuthenticationFailureHandler (post-processor) used for handling a failed client authentication and returning the OAuth2Error response. |
OAuth2ClientAuthenticationConfigurer
configures the OAuth2ClientAuthenticationFilter
and registers it with the OAuth2 authorization server SecurityFilterChain
@Bean
.
OAuth2ClientAuthenticationFilter
is the Filter
that processes client authentication requests.
By default, client authentication is required for the OAuth2 Token endpoint, the OAuth2 Token Introspection endpoint, and the OAuth2 Token Revocation endpoint.
The supported client authentication methods are client_secret_basic
, client_secret_post
, private_key_jwt
, client_secret_jwt
, tls_client_auth
, self_signed_tls_client_auth
, and none
(public clients).
OAuth2ClientAuthenticationFilter
is configured with the following defaults:
-
AuthenticationConverter
— ADelegatingAuthenticationConverter
composed ofJwtClientAssertionAuthenticationConverter
,X509ClientCertificateAuthenticationConverter
,ClientSecretBasicAuthenticationConverter
,ClientSecretPostAuthenticationConverter
, andPublicClientAuthenticationConverter
. -
AuthenticationManager
— AnAuthenticationManager
composed ofJwtClientAssertionAuthenticationProvider
,X509ClientCertificateAuthenticationProvider
,ClientSecretAuthenticationProvider
, andPublicClientAuthenticationProvider
. -
AuthenticationSuccessHandler
— An internal implementation that associates the “authenticated”OAuth2ClientAuthenticationToken
(currentAuthentication
) to theSecurityContext
. -
AuthenticationFailureHandler
— An internal implementation that uses theOAuth2Error
associated with theOAuth2AuthenticationException
to return the OAuth2 error response.
Customizing Jwt Client Assertion Validation
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY
is the default factory that provides an OAuth2TokenValidator<Jwt>
for the specified RegisteredClient
and is used for validating the iss
, sub
, aud
, exp
and nbf
claims of the Jwt
client assertion.
JwtClientAssertionDecoderFactory
provides the ability to override the default Jwt
client assertion validation by supplying a custom factory of type Function<RegisteredClient, OAuth2TokenValidator<Jwt>>
to setJwtValidatorFactory()
.
JwtClientAssertionDecoderFactory is the default JwtDecoderFactory used by JwtClientAssertionAuthenticationProvider that provides a JwtDecoder for the specified RegisteredClient and is used for authenticating a Jwt Bearer Token during OAuth2 client authentication.
|
A common use case for customizing JwtClientAssertionDecoderFactory
is to validate additional claims in the Jwt
client assertion.
The following example shows how to configure JwtClientAssertionAuthenticationProvider
with a customized JwtClientAssertionDecoderFactory
that validates an additional claim in the Jwt
client assertion:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.clientAuthentication(clientAuthentication ->
clientAuthentication
.authenticationProviders(configureJwtClientAssertionValidator())
)
);
return http.build();
}
private Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {
return (authenticationProviders) ->
authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {
// Customize JwtClientAssertionDecoderFactory
JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();
Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->
new DelegatingOAuth2TokenValidator<>(
// Use default validators
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),
// Add custom validator
new JwtClaimValidator<>("claim", "value"::equals));
jwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);
((JwtClientAssertionAuthenticationProvider) authenticationProvider)
.setJwtDecoderFactory(jwtDecoderFactory);
}
});
}
Customizing Mutual-TLS Client Authentication
X509ClientCertificateAuthenticationProvider
is used for authenticating the client X509Certificate
chain received when ClientAuthenticationMethod.TLS_CLIENT_AUTH
or ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
method is used during OAuth2 client authentication.
It is also composed with a "Certificate Verifier", which is used to verify the contents of the client X509Certificate
after the TLS handshake has successfully completed.
PKI Mutual-TLS Method
For the PKI Mutual-TLS (ClientAuthenticationMethod.TLS_CLIENT_AUTH
) method, the default implementation of the certificate verifier verifies the subject distinguished name of the client X509Certificate
against the setting RegisteredClient.getClientSettings.getX509CertificateSubjectDN()
.
If you need to verify another attribute of the client X509Certificate
, for example, a Subject Alternative Name (SAN) entry, the following example shows how to configure X509ClientCertificateAuthenticationProvider
with a custom implementation of a certificate verifier:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.with(authorizationServerConfigurer, (authorizationServer) ->
authorizationServer
.clientAuthentication(clientAuthentication ->
clientAuthentication
.authenticationProviders(configureX509ClientCertificateVerifier())
)
);
return http.build();
}
private Consumer<List<AuthenticationProvider>> configureX509ClientCertificateVerifier() {
return (authenticationProviders) ->
authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof X509ClientCertificateAuthenticationProvider) {
Consumer<OAuth2ClientAuthenticationContext> certificateVerifier = (clientAuthenticationContext) -> {
OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();
RegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();
X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
X509Certificate clientCertificate = clientCertificateChain[0];
// TODO Verify Subject Alternative Name (SAN) entry
};
((X509ClientCertificateAuthenticationProvider) authenticationProvider)
.setCertificateVerifier(certificateVerifier);
}
});
}
Self-Signed Certificate Mutual-TLS Method
For the Self-Signed Certificate Mutual-TLS (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
) method, the default implementation of the certificate verifier will retrieve the client’s JSON Web Key Set using the setting RegisteredClient.getClientSettings.getJwkSetUrl()
and expect to find a match against the client X509Certificate
received during the TLS handshake.
The RegisteredClient.getClientSettings.getJwkSetUrl() setting is used to retrieve the client’s certificates via a JSON Web Key (JWK) Set.
A certificate is represented with the x5c parameter of an individual JWK within the set.
|
Client Certificate-Bound Access Tokens
When Mutual-TLS client authentication is used at the token endpoint, the authorization server is able to bind the issued access token to the client’s X509Certificate
.
The binding is accomplished by computing the SHA-256 thumbprint of the client’s X509Certificate
and associating the thumbprint with the access token.
For example, a JWT access token would include a x5t#S256
claim, containing the X509Certificate
thumbprint, within the top-level cnf
(confirmation method) claim.
Binding the access token to the client’s X509Certificate
provides the ability to implement a proof-of-possession mechanism during protected resource access.
For example, the protected resource would obtain the client’s X509Certificate
used during Mutual-TLS authentication and then verify that the certificate thumbprint matches the x5t#S256
claim associated with the access token.
The following example shows how to enable certificate-bound access tokens for a client:
RegisteredClient mtlsClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("mtls-client")
.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope("scope-a")
.clientSettings(
ClientSettings.builder()
.x509CertificateSubjectDN("CN=mtls-client,OU=Spring Samples,O=Spring,C=US")
.build()
)
.tokenSettings(
TokenSettings.builder()
.x509CertificateBoundAccessTokens(true)
.build()
)
.build();