Add documentation for OAuth 2.0 Pushed Authorization Requests (PAR)
Closes gh-2014
This commit is contained in:
@@ -118,17 +118,18 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
|
||||
.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>
|
||||
.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint -> { }) <8>
|
||||
.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { }) <9>
|
||||
.deviceVerificationEndpoint(deviceVerificationEndpoint -> { }) <10>
|
||||
.tokenEndpoint(tokenEndpoint -> { }) <11>
|
||||
.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { }) <12>
|
||||
.tokenRevocationEndpoint(tokenRevocationEndpoint -> { }) <13>
|
||||
.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { }) <14>
|
||||
.oidc(oidc -> oidc
|
||||
.providerConfigurationEndpoint(providerConfigurationEndpoint -> { }) <14>
|
||||
.logoutEndpoint(logoutEndpoint -> { }) <15>
|
||||
.userInfoEndpoint(userInfoEndpoint -> { }) <16>
|
||||
.clientRegistrationEndpoint(clientRegistrationEndpoint -> { }) <17>
|
||||
.providerConfigurationEndpoint(providerConfigurationEndpoint -> { }) <15>
|
||||
.logoutEndpoint(logoutEndpoint -> { }) <16>
|
||||
.userInfoEndpoint(userInfoEndpoint -> { }) <17>
|
||||
.clientRegistrationEndpoint(clientRegistrationEndpoint -> { }) <18>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -142,16 +143,17 @@ public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity h
|
||||
<5> `tokenGenerator()`: The xref:core-model-components.adoc#oauth2-token-generator[`OAuth2TokenGenerator`] for generating tokens supported by the OAuth2 authorization server.
|
||||
<6> `clientAuthentication()`: The configurer for xref:configuration-model.adoc#configuring-client-authentication[OAuth2 Client Authentication].
|
||||
<7> `authorizationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-authorization-endpoint[OAuth2 Authorization endpoint].
|
||||
<8> `deviceAuthorizationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-device-authorization-endpoint[OAuth2 Device Authorization endpoint].
|
||||
<9> `deviceVerificationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-device-verification-endpoint[OAuth2 Device Verification endpoint].
|
||||
<10> `tokenEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-endpoint[OAuth2 Token endpoint].
|
||||
<11> `tokenIntrospectionEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-introspection-endpoint[OAuth2 Token Introspection endpoint].
|
||||
<12> `tokenRevocationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-revocation-endpoint[OAuth2 Token Revocation endpoint].
|
||||
<13> `authorizationServerMetadataEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-authorization-server-metadata-endpoint[OAuth2 Authorization Server Metadata endpoint].
|
||||
<14> `providerConfigurationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-provider-configuration-endpoint[OpenID Connect 1.0 Provider Configuration endpoint].
|
||||
<15> `logoutEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-logout-endpoint[OpenID Connect 1.0 Logout endpoint].
|
||||
<16> `userInfoEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo endpoint].
|
||||
<17> `clientRegistrationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration endpoint].
|
||||
<8> `pushedAuthorizationRequestEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-pushed-authorization-request-endpoint[OAuth2 Pushed Authorization Request endpoint].
|
||||
<9> `deviceAuthorizationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-device-authorization-endpoint[OAuth2 Device Authorization endpoint].
|
||||
<10> `deviceVerificationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-device-verification-endpoint[OAuth2 Device Verification endpoint].
|
||||
<11> `tokenEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-endpoint[OAuth2 Token endpoint].
|
||||
<12> `tokenIntrospectionEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-introspection-endpoint[OAuth2 Token Introspection endpoint].
|
||||
<13> `tokenRevocationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-token-revocation-endpoint[OAuth2 Token Revocation endpoint].
|
||||
<14> `authorizationServerMetadataEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oauth2-authorization-server-metadata-endpoint[OAuth2 Authorization Server Metadata endpoint].
|
||||
<15> `providerConfigurationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-provider-configuration-endpoint[OpenID Connect 1.0 Provider Configuration endpoint].
|
||||
<16> `logoutEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-logout-endpoint[OpenID Connect 1.0 Logout endpoint].
|
||||
<17> `userInfoEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-user-info-endpoint[OpenID Connect 1.0 UserInfo endpoint].
|
||||
<18> `clientRegistrationEndpoint()`: The configurer for the xref:protocol-endpoints.adoc#oidc-client-registration-endpoint[OpenID Connect 1.0 Client Registration endpoint].
|
||||
|
||||
[[configuring-authorization-server-settings]]
|
||||
== Configuring Authorization Server Settings
|
||||
@@ -169,6 +171,7 @@ public final class AuthorizationServerSettings extends AbstractSettings {
|
||||
public static Builder builder() {
|
||||
return new Builder()
|
||||
.authorizationEndpoint("/oauth2/authorize")
|
||||
.pushedAuthorizationRequestEndpoint("/oauth2/par")
|
||||
.deviceAuthorizationEndpoint("/oauth2/device_authorization")
|
||||
.deviceVerificationEndpoint("/oauth2/device_verification")
|
||||
.tokenEndpoint("/oauth2/token")
|
||||
@@ -200,6 +203,7 @@ public AuthorizationServerSettings authorizationServerSettings() {
|
||||
return AuthorizationServerSettings.builder()
|
||||
.issuer("https://example.com")
|
||||
.authorizationEndpoint("/oauth2/v1/authorize")
|
||||
.pushedAuthorizationRequestEndpoint("/oauth2/v1/par")
|
||||
.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
|
||||
.deviceVerificationEndpoint("/oauth2/v1/device_verification")
|
||||
.tokenEndpoint("/oauth2/v1/token")
|
||||
|
||||
@@ -82,6 +82,7 @@ Spring Authorization Server supports the following features:
|
||||
|xref:protocol-endpoints.adoc[Protocol Endpoints]
|
||||
|
|
||||
* xref:protocol-endpoints.adoc#oauth2-authorization-endpoint[OAuth2 Authorization Endpoint]
|
||||
* xref:protocol-endpoints.adoc#oauth2-pushed-authorization-request-endpoint[OAuth2 Pushed Authorization Request Endpoint]
|
||||
* xref:protocol-endpoints.adoc#oauth2-device-authorization-endpoint[OAuth2 Device Authorization Endpoint]
|
||||
* xref:protocol-endpoints.adoc#oauth2-device-verification-endpoint[OAuth2 Device Verification Endpoint]
|
||||
* xref:protocol-endpoints.adoc#oauth2-token-endpoint[OAuth2 Token Endpoint]
|
||||
@@ -97,6 +98,8 @@ Spring Authorization Server supports the following features:
|
||||
* The OAuth 2.1 Authorization Framework (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07[draft])
|
||||
** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-3.1[Authorization Endpoint]
|
||||
** https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-07#section-3.2[Token Endpoint]
|
||||
* OAuth 2.0 Pushed Authorization Requests (https://datatracker.ietf.org/doc/html/rfc9126[RFC 9126])
|
||||
** https://datatracker.ietf.org/doc/html/rfc9126#section-2[Pushed Authorization Request Endpoint]
|
||||
* OAuth 2.0 Device Authorization Grant (https://tools.ietf.org/html/rfc8628[RFC 8628])
|
||||
** https://tools.ietf.org/html/rfc8628#section-3.1[Device Authorization Endpoint]
|
||||
** https://tools.ietf.org/html/rfc8628#section-3.3[Device Verification Endpoint]
|
||||
|
||||
@@ -126,6 +126,129 @@ static class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationC
|
||||
}
|
||||
----
|
||||
|
||||
[[oauth2-pushed-authorization-request-endpoint]]
|
||||
== OAuth2 Pushed Authorization Request Endpoint
|
||||
|
||||
`OAuth2PushedAuthorizationRequestEndpointConfigurer` provides the ability to customize the https://datatracker.ietf.org/doc/html/rfc9126#section-2[OAuth2 Pushed Authorization Request endpoint].
|
||||
It defines extension points that let you customize the pre-processing, main processing, and post-processing logic for https://datatracker.ietf.org/doc/html/rfc9126#section-2.1[OAuth2 Pushed Authorization requests].
|
||||
|
||||
`OAuth2PushedAuthorizationRequestEndpointConfigurer` provides the following configuration options:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
OAuth2AuthorizationServerConfigurer.authorizationServer();
|
||||
|
||||
http
|
||||
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
|
||||
.with(authorizationServerConfigurer, (authorizationServer) ->
|
||||
authorizationServer
|
||||
.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint ->
|
||||
pushedAuthorizationRequestEndpoint
|
||||
.pushedAuthorizationRequestConverter(pushedAuthorizationRequestConverter) <1>
|
||||
.pushedAuthorizationRequestConverters(pushedAuthorizationRequestConvertersConsumer) <2>
|
||||
.authenticationProvider(authenticationProvider) <3>
|
||||
.authenticationProviders(authenticationProvidersConsumer) <4>
|
||||
.pushedAuthorizationResponseHandler(pushedAuthorizationResponseHandler) <5>
|
||||
.errorResponseHandler(errorResponseHandler) <6>
|
||||
)
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
----
|
||||
<1> `pushedAuthorizationRequestConverter()`: Adds an `AuthenticationConverter` (_pre-processor_) used when attempting to extract an https://datatracker.ietf.org/doc/html/rfc9126#section-2.1[OAuth2 pushed authorization request] from `HttpServletRequest` to an instance of `OAuth2PushedAuthorizationRequestAuthenticationToken`.
|
||||
<2> `pushedAuthorizationRequestConverters()`: 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 `OAuth2PushedAuthorizationRequestAuthenticationToken`.
|
||||
<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> `pushedAuthorizationResponseHandler()`: The `AuthenticationSuccessHandler` (_post-processor_) used for handling an "`authenticated`" `OAuth2PushedAuthorizationRequestAuthenticationToken` and returning the https://datatracker.ietf.org/doc/html/rfc9126#section-2.2[OAuth2 pushed authorization response].
|
||||
<6> `errorResponseHandler()`: The `AuthenticationFailureHandler` (_post-processor_) used for handling an `OAuth2AuthenticationException` and returning the https://datatracker.ietf.org/doc/html/rfc9126#section-2.3[OAuth2Error response].
|
||||
|
||||
`OAuth2PushedAuthorizationRequestEndpointConfigurer` configures the `OAuth2PushedAuthorizationRequestEndpointFilter` and registers it with the OAuth2 authorization server `SecurityFilterChain` `@Bean`.
|
||||
`OAuth2PushedAuthorizationRequestEndpointFilter` is the `Filter` that processes OAuth2 pushed authorization requests.
|
||||
|
||||
`OAuth2PushedAuthorizationRequestEndpointFilter` is configured with the following defaults:
|
||||
|
||||
* `*AuthenticationConverter*` -- A `DelegatingAuthenticationConverter` composed of `OAuth2AuthorizationCodeRequestAuthenticationConverter`.
|
||||
* `*AuthenticationManager*` -- An `AuthenticationManager` composed of `OAuth2PushedAuthorizationRequestAuthenticationProvider`.
|
||||
* `*AuthenticationSuccessHandler*` -- An internal implementation that handles an "`authenticated`" `OAuth2PushedAuthorizationRequestAuthenticationToken` and returns the OAuth2 pushed authorization response.
|
||||
* `*AuthenticationFailureHandler*` -- An `OAuth2ErrorAuthenticationFailureHandler`.
|
||||
|
||||
[[oauth2-pushed-authorization-request-endpoint-customizing-authorization-request-validation]]
|
||||
=== Customizing Pushed Authorization Request Validation
|
||||
|
||||
`OAuth2AuthorizationCodeRequestAuthenticationValidator` is the default validator used for validating specific OAuth2 pushed authorization request parameters used in the Authorization Code Grant.
|
||||
The default implementation validates the `redirect_uri` and `scope` parameters.
|
||||
If validation fails, an `OAuth2AuthorizationCodeRequestAuthenticationException` is thrown.
|
||||
|
||||
`OAuth2PushedAuthorizationRequestAuthenticationProvider` provides the ability to override the default pushed authorization request validation by supplying a custom authentication validator of type `Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>` to `setAuthenticationValidator()`.
|
||||
|
||||
[TIP]
|
||||
`OAuth2AuthorizationCodeRequestAuthenticationContext` holds the `OAuth2AuthorizationCodeRequestAuthenticationToken`, which contains the OAuth2 pushed authorization request parameters.
|
||||
|
||||
[IMPORTANT]
|
||||
If validation fails, the authentication validator *MUST* throw `OAuth2AuthorizationCodeRequestAuthenticationException`.
|
||||
|
||||
A common use case during the development life cycle phase is to allow for `localhost` in the `redirect_uri` parameter.
|
||||
|
||||
The following example shows how to configure `OAuth2PushedAuthorizationRequestAuthenticationProvider` with a custom authentication validator that allows for `localhost` in the `redirect_uri` parameter:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
|
||||
OAuth2AuthorizationServerConfigurer.authorizationServer();
|
||||
|
||||
http
|
||||
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
|
||||
.with(authorizationServerConfigurer, (authorizationServer) ->
|
||||
authorizationServer
|
||||
.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint ->
|
||||
pushedAuthorizationRequestEndpoint
|
||||
.authenticationProviders(configureAuthenticationValidator())
|
||||
)
|
||||
);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> configureAuthenticationValidator() {
|
||||
return (authenticationProviders) ->
|
||||
authenticationProviders.forEach((authenticationProvider) -> {
|
||||
if (authenticationProvider instanceof OAuth2PushedAuthorizationRequestAuthenticationProvider) {
|
||||
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =
|
||||
// Override default redirect_uri validator
|
||||
new CustomRedirectUriValidator()
|
||||
// Reuse default scope validator
|
||||
.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_SCOPE_VALIDATOR);
|
||||
|
||||
((OAuth2PushedAuthorizationRequestAuthenticationProvider) authenticationProvider)
|
||||
.setAuthenticationValidator(authenticationValidator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
|
||||
|
||||
@Override
|
||||
public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationContext.getAuthentication();
|
||||
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
|
||||
String requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();
|
||||
|
||||
// Use exact string matching when comparing client redirect URIs against pre-registered URIs
|
||||
if (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
[[oauth2-device-authorization-endpoint]]
|
||||
== OAuth2 Device Authorization Endpoint
|
||||
|
||||
|
||||
@@ -63,9 +63,10 @@ public class OAuth2PushedAuthorizationRequestAuthenticationToken
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the authenticated client principal
|
||||
* @param requestUri the request URI corresponding to the authorization request posted
|
||||
* @param requestUri the {@code request_uri} corresponding to the authorization
|
||||
* request posted
|
||||
* @param requestUriExpiresAt the expiration time on or after which the
|
||||
* {@code requestUri} MUST NOT be accepted
|
||||
* {@code request_uri} MUST NOT be accepted
|
||||
* @param redirectUri the redirect uri
|
||||
* @param state the state
|
||||
* @param scopes the authorized scope(s)
|
||||
@@ -81,11 +82,21 @@ public class OAuth2PushedAuthorizationRequestAuthenticationToken
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code request_uri} corresponding to the authorization request posted.
|
||||
* @return the {@code request_uri} corresponding to the authorization request posted
|
||||
*/
|
||||
@Nullable
|
||||
public String getRequestUri() {
|
||||
return this.requestUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expiration time on or after which the {@code request_uri} MUST NOT be
|
||||
* accepted.
|
||||
* @return the expiration time on or after which the {@code request_uri} MUST NOT be
|
||||
* accepted
|
||||
*/
|
||||
@Nullable
|
||||
public Instant getRequestUriExpiresAt() {
|
||||
return this.requestUriExpiresAt;
|
||||
|
||||
@@ -22,6 +22,9 @@ import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.StringKeyGenerator;
|
||||
|
||||
/**
|
||||
* A representation of a {@code request_uri} used in OAuth 2.0 Pushed Authorization
|
||||
* Requests.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.5
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user