Merge branch '1.1.x' into 1.2.x
This commit is contained in:
@@ -15,7 +15,7 @@ repositories {
|
||||
dependencies {
|
||||
implementation "com.github.ben-manes:gradle-versions-plugin:0.38.0"
|
||||
implementation "io.github.gradle-nexus:publish-plugin:1.1.0"
|
||||
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.31"
|
||||
implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.41"
|
||||
implementation "io.spring.nohttp:nohttp-gradle:0.0.10"
|
||||
implementation "org.asciidoctor:asciidoctor-gradle-jvm:3.3.2"
|
||||
implementation "org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.3.2"
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.gradle.api.plugins.quality.CheckstylePlugin;
|
||||
public class SpringJavaCheckstylePlugin implements Plugin<Project> {
|
||||
private static final String CHECKSTYLE_DIR = "etc/checkstyle";
|
||||
private static final String SPRING_JAVAFORMAT_VERSION_PROPERTY = "springJavaformatVersion";
|
||||
private static final String DEFAULT_SPRING_JAVAFORMAT_VERSION = "0.0.31";
|
||||
private static final String DEFAULT_SPRING_JAVAFORMAT_VERSION = "0.0.41";
|
||||
private static final String NOHTTP_CHECKSTYLE_VERSION_PROPERTY = "nohttpCheckstyleVersion";
|
||||
private static final String DEFAULT_NOHTTP_CHECKSTYLE_VERSION = "0.0.10";
|
||||
private static final String CHECKSTYLE_TOOL_VERSION_PROPERTY = "checkstyleToolVersion";
|
||||
|
||||
@@ -4,8 +4,7 @@ org.gradle.parallel=true
|
||||
org.gradle.caching=true
|
||||
springFrameworkVersion=6.1.6
|
||||
springSecurityVersion=6.2.4
|
||||
springJavaformatVersion=0.0.38
|
||||
springJavaformatExcludePackages=org/springframework/security/config org/springframework/security/oauth2
|
||||
springJavaformatVersion=0.0.41
|
||||
checkstyleToolVersion=8.34
|
||||
nohttpCheckstyleVersion=0.0.11
|
||||
jacocoToolVersion=0.8.7
|
||||
|
||||
@@ -29,19 +29,27 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A base representation of OAuth 2.0 Authorization Server metadata,
|
||||
* returned by an endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
|
||||
* The metadata endpoint returns a set of claims an Authorization Server describes about its configuration.
|
||||
* A base representation of OAuth 2.0 Authorization Server metadata, returned by an
|
||||
* endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect
|
||||
* Discovery 1.0. The metadata endpoint returns a set of claims an Authorization Server
|
||||
* describes about its configuration.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @see OAuth2AuthorizationServerMetadataClaimAccessor
|
||||
* @since 0.1.1
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2. Authorization Server Metadata Response</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2. OpenID Provider Configuration Response</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2.
|
||||
* Authorization Server Metadata Response</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2.
|
||||
* OpenID Provider Configuration Response</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
|
||||
* Device Authorization Grant Metadata</a>
|
||||
*/
|
||||
public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
|
||||
public abstract class AbstractOAuth2AuthorizationServerMetadata
|
||||
implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Map<String, Object> claims;
|
||||
|
||||
protected AbstractOAuth2AuthorizationServerMetadata(Map<String, Object> claims) {
|
||||
@@ -51,7 +59,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
|
||||
/**
|
||||
* Returns the metadata as claims.
|
||||
*
|
||||
* @return a {@code Map} of the metadata as claims
|
||||
*/
|
||||
@Override
|
||||
@@ -63,6 +70,7 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
* A builder for subclasses of {@link AbstractOAuth2AuthorizationServerMetadata}.
|
||||
*/
|
||||
protected static abstract class AbstractBuilder<T extends AbstractOAuth2AuthorizationServerMetadata, B extends AbstractBuilder<T, B>> {
|
||||
|
||||
private final Map<String, Object> claims = new LinkedHashMap<>();
|
||||
|
||||
protected AbstractBuilder() {
|
||||
@@ -74,12 +82,14 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected final B getThis() {
|
||||
return (B) this; // avoid unchecked casts in subclasses by using "getThis()" instead of "(B) this"
|
||||
// avoid unchecked casts in subclasses by using "getThis()" instead of "(B)
|
||||
// this"
|
||||
return (B) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code issuer} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
*
|
||||
* Use this {@code issuer} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
* @param issuer the {@code URL} of the Authorization Server's Issuer Identifier
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -88,9 +98,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code authorization_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
*
|
||||
* @param authorizationEndpoint the {@code URL} of the OAuth 2.0 Authorization Endpoint
|
||||
* Use this {@code authorization_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
* @param authorizationEndpoint the {@code URL} of the OAuth 2.0 Authorization
|
||||
* Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B authorizationEndpoint(String authorizationEndpoint) {
|
||||
@@ -98,19 +109,21 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code device_authorization_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
|
||||
* Use this {@code device_authorization_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device
|
||||
* Authorization Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
public B deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) {
|
||||
return claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, deviceAuthorizationEndpoint);
|
||||
return claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT,
|
||||
deviceAuthorizationEndpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code token_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
*
|
||||
* Use this {@code token_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
* @param tokenEndpoint the {@code URL} of the OAuth 2.0 Token Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -119,31 +132,35 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this client authentication method to the collection of {@code token_endpoint_auth_methods_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Endpoint
|
||||
* Add this client authentication method to the collection of
|
||||
* {@code token_endpoint_auth_methods_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param authenticationMethod the client authentication method supported by the
|
||||
* OAuth 2.0 Token Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenEndpointAuthenticationMethod(String authenticationMethod) {
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod);
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethod);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Endpoint
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability
|
||||
* to add, replace, or remove.
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client
|
||||
* authentication method(s) supported by the OAuth 2.0 Token Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer);
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethodsConsumer);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code jwks_uri} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* Use this {@code jwks_uri} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param jwkSetUrl the {@code URL} of the JSON Web Key Set
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -153,8 +170,8 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
|
||||
/**
|
||||
* Add this OAuth 2.0 {@code scope} to the collection of {@code scopes_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, RECOMMENDED.
|
||||
*
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata},
|
||||
* RECOMMENDED.
|
||||
* @param scope the OAuth 2.0 {@code scope} value supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -164,9 +181,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code scope} values supported allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values supported
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code scope} values supported allowing the
|
||||
* ability to add, replace, or remove.
|
||||
* @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values
|
||||
* supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B scopes(Consumer<List<String>> scopesConsumer) {
|
||||
@@ -175,9 +193,9 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this OAuth 2.0 {@code response_type} to the collection of {@code response_types_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
*
|
||||
* Add this OAuth 2.0 {@code response_type} to the collection of
|
||||
* {@code response_types_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED.
|
||||
* @param responseType the OAuth 2.0 {@code response_type} value supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -187,20 +205,22 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code response_type} values supported allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code response_type} values supported
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code response_type} values supported
|
||||
* allowing the ability to add, replace, or remove.
|
||||
* @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0
|
||||
* {@code response_type} values supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B responseTypes(Consumer<List<String>> responseTypesConsumer) {
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseTypesConsumer);
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,
|
||||
responseTypesConsumer);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this OAuth 2.0 {@code grant_type} to the collection of {@code grant_types_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* Add this OAuth 2.0 {@code grant_type} to the collection of
|
||||
* {@code grant_types_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param grantType the OAuth 2.0 {@code grant_type} value supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -210,9 +230,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported
|
||||
* allowing the ability to add, replace, or remove.
|
||||
* @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0
|
||||
* {@code grant_type} values supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B grantTypes(Consumer<List<String>> grantTypesConsumer) {
|
||||
@@ -221,9 +242,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code revocation_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param tokenRevocationEndpoint the {@code URL} of the OAuth 2.0 Token Revocation Endpoint
|
||||
* Use this {@code revocation_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param tokenRevocationEndpoint the {@code URL} of the OAuth 2.0 Token
|
||||
* Revocation Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenRevocationEndpoint(String tokenRevocationEndpoint) {
|
||||
@@ -231,65 +253,78 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this client authentication method to the collection of {@code revocation_endpoint_auth_methods_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
* Add this client authentication method to the collection of
|
||||
* {@code revocation_endpoint_auth_methods_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param authenticationMethod the client authentication method supported by the
|
||||
* OAuth 2.0 Token Revocation Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenRevocationEndpointAuthenticationMethod(String authenticationMethod) {
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod);
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethod);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability
|
||||
* to add, replace, or remove.
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client
|
||||
* authentication method(s) supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenRevocationEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer);
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethodsConsumer);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code introspection_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param tokenIntrospectionEndpoint the {@code URL} of the OAuth 2.0 Token Introspection Endpoint
|
||||
* Use this {@code introspection_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param tokenIntrospectionEndpoint the {@code URL} of the OAuth 2.0 Token
|
||||
* Introspection Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenIntrospectionEndpoint(String tokenIntrospectionEndpoint) {
|
||||
return claim(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, tokenIntrospectionEndpoint);
|
||||
return claim(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT,
|
||||
tokenIntrospectionEndpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this client authentication method to the collection of {@code introspection_endpoint_auth_methods_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Introspection Endpoint
|
||||
* Add this client authentication method to the collection of
|
||||
* {@code introspection_endpoint_auth_methods_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param authenticationMethod the client authentication method supported by the
|
||||
* OAuth 2.0 Token Introspection Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenIntrospectionEndpointAuthenticationMethod(String authenticationMethod) {
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod);
|
||||
addClaimToClaimList(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethod);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Introspection Endpoint
|
||||
* A {@code Consumer} of the client authentication method(s) allowing the ability
|
||||
* to add, replace, or remove.
|
||||
* @param authenticationMethodsConsumer a {@code Consumer} of the client
|
||||
* authentication method(s) supported by the OAuth 2.0 Token Introspection
|
||||
* Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B tokenIntrospectionEndpointAuthenticationMethods(Consumer<List<String>> authenticationMethodsConsumer) {
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer);
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
authenticationMethodsConsumer);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code registration_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* @param clientRegistrationEndpoint the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint
|
||||
* Use this {@code registration_endpoint} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param clientRegistrationEndpoint the {@code URL} of the OAuth 2.0 Dynamic
|
||||
* Client Registration Endpoint
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -298,31 +333,35 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this Proof Key for Code Exchange (PKCE) {@code code_challenge_method} to the collection of {@code code_challenge_methods_supported}
|
||||
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
*
|
||||
* Add this Proof Key for Code Exchange (PKCE) {@code code_challenge_method} to
|
||||
* the collection of {@code code_challenge_methods_supported} in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
|
||||
* @param codeChallengeMethod the {@code code_challenge_method} value supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B codeChallengeMethod(String codeChallengeMethod) {
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, codeChallengeMethod);
|
||||
addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,
|
||||
codeChallengeMethod);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param codeChallengeMethodsConsumer a {@code Consumer} of the {@code code_challenge_method} values supported
|
||||
* A {@code Consumer} of the Proof Key for Code Exchange (PKCE)
|
||||
* {@code code_challenge_method} values supported allowing the ability to add,
|
||||
* replace, or remove.
|
||||
* @param codeChallengeMethodsConsumer a {@code Consumer} of the
|
||||
* {@code code_challenge_method} values supported
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
public B codeChallengeMethods(Consumer<List<String>> codeChallengeMethodsConsumer) {
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, codeChallengeMethodsConsumer);
|
||||
acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,
|
||||
codeChallengeMethodsConsumer);
|
||||
return getThis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this claim in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}.
|
||||
*
|
||||
* Use this claim in the resulting
|
||||
* {@link AbstractOAuth2AuthorizationServerMetadata}.
|
||||
* @param name the claim name
|
||||
* @param value the claim value
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
@@ -337,7 +376,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
/**
|
||||
* Provides access to every {@link #claim(String, Object)} declared so far with
|
||||
* the possibility to add, replace, or remove.
|
||||
*
|
||||
* @param claimsConsumer a {@code Consumer} of the claims
|
||||
* @return the {@link AbstractBuilder} for further configurations
|
||||
*/
|
||||
@@ -348,59 +386,105 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
|
||||
/**
|
||||
* Creates the {@link AbstractOAuth2AuthorizationServerMetadata}.
|
||||
*
|
||||
* @return the {@link AbstractOAuth2AuthorizationServerMetadata}
|
||||
*/
|
||||
public abstract T build();
|
||||
|
||||
protected void validate() {
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), "issuer cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), "issuer must be a valid URL");
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint must be a valid URL");
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER),
|
||||
"issuer cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER),
|
||||
"issuer must be a valid URL");
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT),
|
||||
"authorizationEndpoint cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT),
|
||||
"authorizationEndpoint must be a valid URL");
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT), "deviceAuthorizationEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT),
|
||||
"deviceAuthorizationEndpoint must be a valid URL");
|
||||
}
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint must be a valid URL");
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenEndpointAuthenticationMethods cannot be empty");
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT),
|
||||
"tokenEndpoint cannot be null");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT),
|
||||
"tokenEndpoint must be a valid URL");
|
||||
if (getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenEndpointAuthenticationMethods cannot be empty");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) != null) {
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI), "jwksUri must be a valid URL");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI),
|
||||
"jwksUri must be a valid URL");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes cannot be empty");
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED),
|
||||
"scopes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED),
|
||||
"scopes cannot be empty");
|
||||
}
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be null");
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be empty");
|
||||
Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),
|
||||
"responseTypes cannot be null");
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),
|
||||
"responseTypes must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED),
|
||||
"responseTypes cannot be empty");
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), "grantTypes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), "grantTypes cannot be empty");
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED),
|
||||
"grantTypes must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED),
|
||||
"grantTypes cannot be empty");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT), "tokenRevocationEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT),
|
||||
"tokenRevocationEndpoint must be a valid URL");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenRevocationEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenRevocationEndpointAuthenticationMethods cannot be empty");
|
||||
if (getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenRevocationEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims().get(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenRevocationEndpointAuthenticationMethods cannot be empty");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT), "tokenIntrospectionEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT),
|
||||
"tokenIntrospectionEndpoint must be a valid URL");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenIntrospectionEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenIntrospectionEndpointAuthenticationMethods cannot be empty");
|
||||
if (getClaims().get(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenIntrospectionEndpointAuthenticationMethods must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED),
|
||||
"tokenIntrospectionEndpointAuthenticationMethods cannot be empty");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT), "clientRegistrationEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT),
|
||||
"clientRegistrationEndpoint must be a valid URL");
|
||||
}
|
||||
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED) != null) {
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), "codeChallengeMethods must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), "codeChallengeMethods cannot be empty");
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED),
|
||||
"codeChallengeMethods must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims()
|
||||
.get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED),
|
||||
"codeChallengeMethods cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,10 +512,12 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
|
||||
|
||||
try {
|
||||
new URI(url.toString()).toURL();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(errorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthorizationConsentService} that stores {@link OAuth2AuthorizationConsent}'s in-memory.
|
||||
* An {@link OAuth2AuthorizationConsentService} that stores
|
||||
* {@link OAuth2AuthorizationConsent}'s in-memory.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This implementation should ONLY be used during development/testing.
|
||||
@@ -36,6 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationConsentService
|
||||
*/
|
||||
public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService {
|
||||
|
||||
private final Map<Integer, OAuth2AuthorizationConsent> authorizationConsents = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
@@ -46,8 +48,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided
|
||||
* parameters.
|
||||
* @param authorizationConsents the authorization consent(s)
|
||||
*/
|
||||
public InMemoryOAuth2AuthorizationConsentService(OAuth2AuthorizationConsent... authorizationConsents) {
|
||||
@@ -55,8 +57,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided
|
||||
* parameters.
|
||||
* @param authorizationConsents the authorization consent(s)
|
||||
*/
|
||||
public InMemoryOAuth2AuthorizationConsentService(List<OAuth2AuthorizationConsent> authorizationConsents) {
|
||||
@@ -66,8 +68,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au
|
||||
int id = getId(authorizationConsent);
|
||||
Assert.isTrue(!this.authorizationConsents.containsKey(id),
|
||||
"The authorizationConsent must be unique. Found duplicate, with registered client id: ["
|
||||
+ authorizationConsent.getRegisteredClientId()
|
||||
+ "] and principal name: [" + authorizationConsent.getPrincipalName() + "]");
|
||||
+ authorizationConsent.getRegisteredClientId() + "] and principal name: ["
|
||||
+ authorizationConsent.getPrincipalName() + "]");
|
||||
this.authorizationConsents.put(id, authorizationConsent);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s in-memory.
|
||||
* An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s
|
||||
* in-memory.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This implementation should ONLY be used during development/testing.
|
||||
@@ -44,15 +45,17 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationService
|
||||
*/
|
||||
public final class InMemoryOAuth2AuthorizationService implements OAuth2AuthorizationService {
|
||||
|
||||
private int maxInitializedAuthorizations = 100;
|
||||
|
||||
/*
|
||||
* Stores "initialized" (uncompleted) authorizations, where an access token has not yet been granted.
|
||||
* This state occurs with the authorization_code grant flow during the user consent step OR
|
||||
* when the code is returned in the authorization response but the access token request is not yet initiated.
|
||||
* Stores "initialized" (uncompleted) authorizations, where an access token has not
|
||||
* yet been granted. This state occurs with the authorization_code grant flow during
|
||||
* the user consent step OR when the code is returned in the authorization response
|
||||
* but the access token request is not yet initiated.
|
||||
*/
|
||||
private Map<String, OAuth2Authorization> initializedAuthorizations =
|
||||
Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
|
||||
private Map<String, OAuth2Authorization> initializedAuthorizations = Collections
|
||||
.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
|
||||
|
||||
/*
|
||||
* Stores "completed" authorizations, where an access token has been granted.
|
||||
@@ -64,7 +67,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
*/
|
||||
InMemoryOAuth2AuthorizationService(int maxInitializedAuthorizations) {
|
||||
this.maxInitializedAuthorizations = maxInitializedAuthorizations;
|
||||
this.initializedAuthorizations = Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
|
||||
this.initializedAuthorizations = Collections
|
||||
.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +79,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided
|
||||
* parameters.
|
||||
* @param authorizations the authorization(s)
|
||||
*/
|
||||
public InMemoryOAuth2AuthorizationService(OAuth2Authorization... authorizations) {
|
||||
@@ -84,8 +88,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided
|
||||
* parameters.
|
||||
* @param authorizations the authorization(s)
|
||||
*/
|
||||
public InMemoryOAuth2AuthorizationService(List<OAuth2Authorization> authorizations) {
|
||||
@@ -103,7 +107,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
Assert.notNull(authorization, "authorization cannot be null");
|
||||
if (isComplete(authorization)) {
|
||||
this.authorizations.put(authorization.getId(), authorization);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.initializedAuthorizations.put(authorization.getId(), authorization);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +118,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
Assert.notNull(authorization, "authorization cannot be null");
|
||||
if (isComplete(authorization)) {
|
||||
this.authorizations.remove(authorization.getId(), authorization);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.initializedAuthorizations.remove(authorization.getId(), authorization);
|
||||
}
|
||||
}
|
||||
@@ -123,9 +129,7 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
public OAuth2Authorization findById(String id) {
|
||||
Assert.hasText(id, "id cannot be empty");
|
||||
OAuth2Authorization authorization = this.authorizations.get(id);
|
||||
return authorization != null ?
|
||||
authorization :
|
||||
this.initializedAuthorizations.get(id);
|
||||
return authorization != null ? authorization : this.initializedAuthorizations.get(id);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -149,7 +153,9 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
return authorization.getAccessToken() != null;
|
||||
}
|
||||
|
||||
private static boolean hasToken(OAuth2Authorization authorization, String token, @Nullable OAuth2TokenType tokenType) {
|
||||
private static boolean hasToken(OAuth2Authorization authorization, String token,
|
||||
@Nullable OAuth2TokenType tokenType) {
|
||||
// @formatter:off
|
||||
if (tokenType == null) {
|
||||
return matchesState(authorization, token) ||
|
||||
matchesAuthorizationCode(authorization, token) ||
|
||||
@@ -173,6 +179,7 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
} else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {
|
||||
return matchesUserCode(authorization, token);
|
||||
}
|
||||
// @formatter:on
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -181,42 +188,38 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza
|
||||
}
|
||||
|
||||
private static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode =
|
||||
authorization.getToken(OAuth2AuthorizationCode.class);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2AccessToken> accessToken =
|
||||
authorization.getToken(OAuth2AccessToken.class);
|
||||
OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);
|
||||
return accessToken != null && accessToken.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken =
|
||||
authorization.getToken(OAuth2RefreshToken.class);
|
||||
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getToken(OAuth2RefreshToken.class);
|
||||
return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesIdToken(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OidcIdToken> idToken =
|
||||
authorization.getToken(OidcIdToken.class);
|
||||
OAuth2Authorization.Token<OidcIdToken> idToken = authorization.getToken(OidcIdToken.class);
|
||||
return idToken != null && idToken.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesDeviceCode(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2DeviceCode> deviceCode =
|
||||
authorization.getToken(OAuth2DeviceCode.class);
|
||||
OAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);
|
||||
return deviceCode != null && deviceCode.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static boolean matchesUserCode(OAuth2Authorization authorization, String token) {
|
||||
OAuth2Authorization.Token<OAuth2UserCode> userCode =
|
||||
authorization.getToken(OAuth2UserCode.class);
|
||||
OAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);
|
||||
return userCode != null && userCode.getToken().getTokenValue().equals(token);
|
||||
}
|
||||
|
||||
private static final class MaxSizeHashMap<K, V> extends LinkedHashMap<K, V> {
|
||||
|
||||
private final int maxSize;
|
||||
|
||||
private MaxSizeHashMap(int maxSize) {
|
||||
|
||||
@@ -47,16 +47,18 @@ import org.springframework.util.StringUtils;
|
||||
* {@link JdbcOperations} for {@link OAuth2AuthorizationConsent} persistence.
|
||||
*
|
||||
* <p>
|
||||
* <b>IMPORTANT:</b> This {@code OAuth2AuthorizationConsentService} depends on the table definition
|
||||
* described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql" and
|
||||
* therefore MUST be defined in the database schema.
|
||||
* <b>IMPORTANT:</b> This {@code OAuth2AuthorizationConsentService} depends on the table
|
||||
* definition described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql"
|
||||
* and therefore MUST be defined in the database schema.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This {@code OAuth2AuthorizationConsentService} is a simplified JDBC implementation that MAY be used in a production environment.
|
||||
* However, it does have limitations as it likely won't perform well in an environment requiring high throughput.
|
||||
* The expectation is that the consuming application will provide their own implementation of {@code OAuth2AuthorizationConsentService}
|
||||
* that meets the performance requirements for its deployment environment.
|
||||
* <b>NOTE:</b> This {@code OAuth2AuthorizationConsentService} is a simplified JDBC
|
||||
* implementation that MAY be used in a production environment. However, it does have
|
||||
* limitations as it likely won't perform well in an environment requiring high
|
||||
* throughput. The expectation is that the consuming application will provide their own
|
||||
* implementation of {@code OAuth2AuthorizationConsentService} that meets the performance
|
||||
* requirements for its deployment environment.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Josh Long
|
||||
@@ -109,13 +111,15 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
private static final String REMOVE_AUTHORIZATION_CONSENT_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + PK_FILTER;
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private RowMapper<OAuth2AuthorizationConsent> authorizationConsentRowMapper;
|
||||
|
||||
private Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> authorizationConsentParametersMapper;
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcOAuth2AuthorizationConsentService} using the provided parameters.
|
||||
*
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* Constructs a {@code JdbcOAuth2AuthorizationConsentService} using the provided
|
||||
* parameters.
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* @param registeredClientRepository the registered client repository
|
||||
*/
|
||||
public JdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations,
|
||||
@@ -130,11 +134,12 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
@Override
|
||||
public void save(OAuth2AuthorizationConsent authorizationConsent) {
|
||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
||||
OAuth2AuthorizationConsent existingAuthorizationConsent = findById(
|
||||
authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName());
|
||||
OAuth2AuthorizationConsent existingAuthorizationConsent = findById(authorizationConsent.getRegisteredClientId(),
|
||||
authorizationConsent.getPrincipalName());
|
||||
if (existingAuthorizationConsent == null) {
|
||||
insertAuthorizationConsent(authorizationConsent);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
updateAuthorizationConsent(authorizationConsent);
|
||||
}
|
||||
}
|
||||
@@ -160,8 +165,7 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||
new SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()),
|
||||
new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName())
|
||||
};
|
||||
new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) };
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||
this.jdbcOperations.update(REMOVE_AUTHORIZATION_CONSENT_SQL, pss);
|
||||
}
|
||||
@@ -173,7 +177,7 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
Assert.hasText(principalName, "principalName cannot be empty");
|
||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClientId),
|
||||
new SqlParameterValue(Types.VARCHAR, principalName)};
|
||||
new SqlParameterValue(Types.VARCHAR, principalName) };
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||
List<OAuth2AuthorizationConsent> result = this.jdbcOperations.query(LOAD_AUTHORIZATION_CONSENT_SQL, pss,
|
||||
this.authorizationConsentRowMapper);
|
||||
@@ -184,22 +188,21 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
* Sets the {@link RowMapper} used for mapping the current row in
|
||||
* {@code java.sql.ResultSet} to {@link OAuth2AuthorizationConsent}. The default is
|
||||
* {@link OAuth2AuthorizationConsentRowMapper}.
|
||||
*
|
||||
* @param authorizationConsentRowMapper the {@link RowMapper} used for mapping the current
|
||||
* row in {@code ResultSet} to {@link OAuth2AuthorizationConsent}
|
||||
* @param authorizationConsentRowMapper the {@link RowMapper} used for mapping the
|
||||
* current row in {@code ResultSet} to {@link OAuth2AuthorizationConsent}
|
||||
*/
|
||||
public final void setAuthorizationConsentRowMapper(RowMapper<OAuth2AuthorizationConsent> authorizationConsentRowMapper) {
|
||||
public final void setAuthorizationConsentRowMapper(
|
||||
RowMapper<OAuth2AuthorizationConsent> authorizationConsentRowMapper) {
|
||||
Assert.notNull(authorizationConsentRowMapper, "authorizationConsentRowMapper cannot be null");
|
||||
this.authorizationConsentRowMapper = authorizationConsentRowMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Function} used for mapping {@link OAuth2AuthorizationConsent} to
|
||||
* a {@code List} of {@link SqlParameterValue}. The default is
|
||||
* Sets the {@code Function} used for mapping {@link OAuth2AuthorizationConsent} to a
|
||||
* {@code List} of {@link SqlParameterValue}. The default is
|
||||
* {@link OAuth2AuthorizationConsentParametersMapper}.
|
||||
*
|
||||
* @param authorizationConsentParametersMapper the {@code Function} used for mapping
|
||||
* {@link OAuth2AuthorizationConsent} to a {@code List} of {@link SqlParameterValue}
|
||||
* {@link OAuth2AuthorizationConsent} to a {@code List} of {@link SqlParameterValue}
|
||||
*/
|
||||
public final void setAuthorizationConsentParametersMapper(
|
||||
Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> authorizationConsentParametersMapper) {
|
||||
@@ -220,10 +223,11 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
}
|
||||
|
||||
/**
|
||||
* The default {@link RowMapper} that maps the current row in
|
||||
* {@code ResultSet} to {@link OAuth2AuthorizationConsent}.
|
||||
* The default {@link RowMapper} that maps the current row in {@code ResultSet} to
|
||||
* {@link OAuth2AuthorizationConsent}.
|
||||
*/
|
||||
public static class OAuth2AuthorizationConsentRowMapper implements RowMapper<OAuth2AuthorizationConsent> {
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
public OAuth2AuthorizationConsentRowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
@@ -236,13 +240,14 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
String registeredClientId = rs.getString("registered_client_id");
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId);
|
||||
if (registeredClient == null) {
|
||||
throw new DataRetrievalFailureException(
|
||||
"The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository.");
|
||||
throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId
|
||||
+ "' was not found in the RegisteredClientRepository.");
|
||||
}
|
||||
|
||||
String principalName = rs.getString("principal_name");
|
||||
|
||||
OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, principalName);
|
||||
OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId,
|
||||
principalName);
|
||||
String authorizationConsentAuthorities = rs.getString("authorities");
|
||||
if (authorizationConsentAuthorities != null) {
|
||||
for (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) {
|
||||
@@ -262,7 +267,8 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
* The default {@code Function} that maps {@link OAuth2AuthorizationConsent} to a
|
||||
* {@code List} of {@link SqlParameterValue}.
|
||||
*/
|
||||
public static class OAuth2AuthorizationConsentParametersMapper implements Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> {
|
||||
public static class OAuth2AuthorizationConsentParametersMapper
|
||||
implements Function<OAuth2AuthorizationConsent, List<SqlParameterValue>> {
|
||||
|
||||
@Override
|
||||
public List<SqlParameterValue> apply(OAuth2AuthorizationConsent authorizationConsent) {
|
||||
@@ -274,7 +280,8 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio
|
||||
for (GrantedAuthority authority : authorizationConsent.getAuthorities()) {
|
||||
authorities.add(authority.getAuthority());
|
||||
}
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToDelimitedString(authorities, ",")));
|
||||
parameters
|
||||
.add(new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToDelimitedString(authorities, ",")));
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,16 +72,18 @@ import org.springframework.util.StringUtils;
|
||||
* {@link JdbcOperations} for {@link OAuth2Authorization} persistence.
|
||||
*
|
||||
* <p>
|
||||
* <b>IMPORTANT:</b> This {@code OAuth2AuthorizationService} depends on the table definition
|
||||
* described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql" and
|
||||
* therefore MUST be defined in the database schema.
|
||||
* <b>IMPORTANT:</b> This {@code OAuth2AuthorizationService} depends on the table
|
||||
* definition described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql"
|
||||
* and therefore MUST be defined in the database schema.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This {@code OAuth2AuthorizationService} is a simplified JDBC implementation that MAY be used in a production environment.
|
||||
* However, it does have limitations as it likely won't perform well in an environment requiring high throughput.
|
||||
* The expectation is that the consuming application will provide their own implementation of {@code OAuth2AuthorizationService}
|
||||
* that meets the performance requirements for its deployment environment.
|
||||
* <b>NOTE:</b> This {@code OAuth2AuthorizationService} is a simplified JDBC
|
||||
* implementation that MAY be used in a production environment. However, it does have
|
||||
* limitations as it likely won't perform well in an environment requiring high
|
||||
* throughput. The expectation is that the consuming application will provide their own
|
||||
* implementation of {@code OAuth2AuthorizationService} that meets the performance
|
||||
* requirements for its deployment environment.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
@@ -144,16 +146,23 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
private static final String TABLE_NAME = "oauth2_authorization";
|
||||
|
||||
private static final String PK_FILTER = "id = ?";
|
||||
|
||||
private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorization_code_value = ? OR "
|
||||
+ "access_token_value = ? OR oidc_id_token_value = ? OR refresh_token_value = ? OR user_code_value = ? OR "
|
||||
+ "device_code_value = ?";
|
||||
|
||||
private static final String STATE_FILTER = "state = ?";
|
||||
|
||||
private static final String AUTHORIZATION_CODE_FILTER = "authorization_code_value = ?";
|
||||
|
||||
private static final String ACCESS_TOKEN_FILTER = "access_token_value = ?";
|
||||
|
||||
private static final String ID_TOKEN_FILTER = "oidc_id_token_value = ?";
|
||||
|
||||
private static final String REFRESH_TOKEN_FILTER = "refresh_token_value = ?";
|
||||
|
||||
private static final String USER_CODE_FILTER = "user_code_value = ?";
|
||||
|
||||
private static final String DEVICE_CODE_FILTER = "device_code_value = ?";
|
||||
|
||||
// @formatter:off
|
||||
@@ -184,14 +193,16 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
private static Map<String, ColumnMetadata> columnMetadataMap;
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private final LobHandler lobHandler;
|
||||
|
||||
private RowMapper<OAuth2Authorization> authorizationRowMapper;
|
||||
|
||||
private Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper;
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters.
|
||||
*
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* @param registeredClientRepository the registered client repository
|
||||
*/
|
||||
public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,
|
||||
@@ -201,10 +212,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters.
|
||||
*
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* @param jdbcOperations the JDBC operations
|
||||
* @param registeredClientRepository the registered client repository
|
||||
* @param lobHandler the handler for large binary fields and large text fields
|
||||
* @param lobHandler the handler for large binary fields and large text fields
|
||||
*/
|
||||
public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations,
|
||||
RegisteredClientRepository registeredClientRepository, LobHandler lobHandler) {
|
||||
@@ -213,7 +223,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
Assert.notNull(lobHandler, "lobHandler cannot be null");
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.lobHandler = lobHandler;
|
||||
OAuth2AuthorizationRowMapper authorizationRowMapper = new OAuth2AuthorizationRowMapper(registeredClientRepository);
|
||||
OAuth2AuthorizationRowMapper authorizationRowMapper = new OAuth2AuthorizationRowMapper(
|
||||
registeredClientRepository);
|
||||
authorizationRowMapper.setLobHandler(lobHandler);
|
||||
this.authorizationRowMapper = authorizationRowMapper;
|
||||
this.authorizationParametersMapper = new OAuth2AuthorizationParametersMapper();
|
||||
@@ -226,7 +237,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
OAuth2Authorization existingAuthorization = findById(authorization.getId());
|
||||
if (existingAuthorization == null) {
|
||||
insertAuthorization(authorization);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
updateAuthorization(authorization);
|
||||
}
|
||||
}
|
||||
@@ -255,8 +267,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
public void remove(OAuth2Authorization authorization) {
|
||||
Assert.notNull(authorization, "authorization cannot be null");
|
||||
SqlParameterValue[] parameters = new SqlParameterValue[] {
|
||||
new SqlParameterValue(Types.VARCHAR, authorization.getId())
|
||||
};
|
||||
new SqlParameterValue(Types.VARCHAR, authorization.getId()) };
|
||||
PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters);
|
||||
this.jdbcOperations.update(REMOVE_AUTHORIZATION_SQL, pss);
|
||||
}
|
||||
@@ -284,25 +295,32 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
parameters.add(mapToSqlParameter("user_code_value", token));
|
||||
parameters.add(mapToSqlParameter("device_code_value", token));
|
||||
return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters);
|
||||
} else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
|
||||
}
|
||||
else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, token));
|
||||
return findBy(STATE_FILTER, parameters);
|
||||
} else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
|
||||
}
|
||||
else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
|
||||
parameters.add(mapToSqlParameter("authorization_code_value", token));
|
||||
return findBy(AUTHORIZATION_CODE_FILTER, parameters);
|
||||
} else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
|
||||
}
|
||||
else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
|
||||
parameters.add(mapToSqlParameter("access_token_value", token));
|
||||
return findBy(ACCESS_TOKEN_FILTER, parameters);
|
||||
} else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
|
||||
}
|
||||
else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
|
||||
parameters.add(mapToSqlParameter("oidc_id_token_value", token));
|
||||
return findBy(ID_TOKEN_FILTER, parameters);
|
||||
} else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
|
||||
}
|
||||
else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
|
||||
parameters.add(mapToSqlParameter("refresh_token_value", token));
|
||||
return findBy(REFRESH_TOKEN_FILTER, parameters);
|
||||
} else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {
|
||||
}
|
||||
else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {
|
||||
parameters.add(mapToSqlParameter("user_code_value", token));
|
||||
return findBy(USER_CODE_FILTER, parameters);
|
||||
} else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {
|
||||
}
|
||||
else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {
|
||||
parameters.add(mapToSqlParameter("device_code_value", token));
|
||||
return findBy(DEVICE_CODE_FILTER, parameters);
|
||||
}
|
||||
@@ -313,7 +331,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
try (LobCreator lobCreator = getLobHandler().getLobCreator()) {
|
||||
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
|
||||
parameters.toArray());
|
||||
List<OAuth2Authorization> result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, pss, getAuthorizationRowMapper());
|
||||
List<OAuth2Authorization> result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, pss,
|
||||
getAuthorizationRowMapper());
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
}
|
||||
@@ -322,9 +341,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
* Sets the {@link RowMapper} used for mapping the current row in
|
||||
* {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is
|
||||
* {@link OAuth2AuthorizationRowMapper}.
|
||||
*
|
||||
* @param authorizationRowMapper the {@link RowMapper} used for mapping the current
|
||||
* row in {@code ResultSet} to {@link OAuth2Authorization}
|
||||
* row in {@code ResultSet} to {@link OAuth2Authorization}
|
||||
*/
|
||||
public final void setAuthorizationRowMapper(RowMapper<OAuth2Authorization> authorizationRowMapper) {
|
||||
Assert.notNull(authorizationRowMapper, "authorizationRowMapper cannot be null");
|
||||
@@ -332,12 +350,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Function} used for mapping {@link OAuth2Authorization} to
|
||||
* a {@code List} of {@link SqlParameterValue}. The default is
|
||||
* Sets the {@code Function} used for mapping {@link OAuth2Authorization} to a
|
||||
* {@code List} of {@link SqlParameterValue}. The default is
|
||||
* {@link OAuth2AuthorizationParametersMapper}.
|
||||
*
|
||||
* @param authorizationParametersMapper the {@code Function} used for mapping
|
||||
* {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue}
|
||||
* {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue}
|
||||
*/
|
||||
public final void setAuthorizationParametersMapper(
|
||||
Function<OAuth2Authorization, List<SqlParameterValue>> authorizationParametersMapper) {
|
||||
@@ -366,8 +383,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
* {@code java.sql.ResultSet} to {@link OAuth2Authorization}.
|
||||
*/
|
||||
public static class OAuth2AuthorizationRowMapper implements RowMapper<OAuth2Authorization> {
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private LobHandler lobHandler = new DefaultLobHandler();
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) {
|
||||
@@ -386,8 +406,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
String registeredClientId = rs.getString("registered_client_id");
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId);
|
||||
if (registeredClient == null) {
|
||||
throw new DataRetrievalFailureException(
|
||||
"The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository.");
|
||||
throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId
|
||||
+ "' was not found in the RegisteredClientRepository.");
|
||||
}
|
||||
|
||||
OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient);
|
||||
@@ -402,10 +422,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
Map<String, Object> attributes = parseMap(getLobValue(rs, "attributes"));
|
||||
|
||||
builder.id(id)
|
||||
.principalName(principalName)
|
||||
.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attributes((attrs) -> attrs.putAll(attributes));
|
||||
.principalName(principalName)
|
||||
.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attributes((attrs) -> attrs.putAll(attributes));
|
||||
|
||||
String state = rs.getString("state");
|
||||
if (StringUtils.hasText(state)) {
|
||||
@@ -419,10 +439,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
if (StringUtils.hasText(authorizationCodeValue)) {
|
||||
tokenIssuedAt = rs.getTimestamp("authorization_code_issued_at").toInstant();
|
||||
tokenExpiresAt = rs.getTimestamp("authorization_code_expires_at").toInstant();
|
||||
Map<String, Object> authorizationCodeMetadata = parseMap(getLobValue(rs, "authorization_code_metadata"));
|
||||
Map<String, Object> authorizationCodeMetadata = parseMap(
|
||||
getLobValue(rs, "authorization_code_metadata"));
|
||||
|
||||
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(
|
||||
authorizationCodeValue, tokenIssuedAt, tokenExpiresAt);
|
||||
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(authorizationCodeValue,
|
||||
tokenIssuedAt, tokenExpiresAt);
|
||||
builder.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata));
|
||||
}
|
||||
|
||||
@@ -441,7 +462,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
if (accessTokenScopes != null) {
|
||||
scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes);
|
||||
}
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, accessTokenValue, tokenIssuedAt, tokenExpiresAt, scopes);
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, accessTokenValue, tokenIssuedAt,
|
||||
tokenExpiresAt, scopes);
|
||||
builder.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata));
|
||||
}
|
||||
|
||||
@@ -451,8 +473,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
tokenExpiresAt = rs.getTimestamp("oidc_id_token_expires_at").toInstant();
|
||||
Map<String, Object> oidcTokenMetadata = parseMap(getLobValue(rs, "oidc_id_token_metadata"));
|
||||
|
||||
OidcIdToken oidcToken = new OidcIdToken(
|
||||
oidcIdTokenValue, tokenIssuedAt, tokenExpiresAt, (Map<String, Object>) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME));
|
||||
OidcIdToken oidcToken = new OidcIdToken(oidcIdTokenValue, tokenIssuedAt, tokenExpiresAt,
|
||||
(Map<String, Object>) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME));
|
||||
builder.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata));
|
||||
}
|
||||
|
||||
@@ -466,8 +488,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
}
|
||||
Map<String, Object> refreshTokenMetadata = parseMap(getLobValue(rs, "refresh_token_metadata"));
|
||||
|
||||
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(
|
||||
refreshTokenValue, tokenIssuedAt, tokenExpiresAt);
|
||||
OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(refreshTokenValue, tokenIssuedAt,
|
||||
tokenExpiresAt);
|
||||
builder.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata));
|
||||
}
|
||||
|
||||
@@ -502,9 +524,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
if (columnValueBytes != null) {
|
||||
columnValue = new String(columnValueBytes, StandardCharsets.UTF_8);
|
||||
}
|
||||
} else if (Types.CLOB == columnMetadata.getDataType()) {
|
||||
}
|
||||
else if (Types.CLOB == columnMetadata.getDataType()) {
|
||||
columnValue = this.lobHandler.getClobAsString(rs, columnName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
columnValue = rs.getString(columnName);
|
||||
}
|
||||
return columnValue;
|
||||
@@ -534,8 +558,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return this.objectMapper.readValue(data, new TypeReference<Map<String, Object>>() {});
|
||||
} catch (Exception ex) {
|
||||
return this.objectMapper.readValue(data, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -546,7 +572,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
* The default {@code Function} that maps {@link OAuth2Authorization} to a
|
||||
* {@code List} of {@link SqlParameterValue}.
|
||||
*/
|
||||
public static class OAuth2AuthorizationParametersMapper implements Function<OAuth2Authorization, List<SqlParameterValue>> {
|
||||
public static class OAuth2AuthorizationParametersMapper
|
||||
implements Function<OAuth2Authorization, List<SqlParameterValue>> {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public OAuth2AuthorizationParametersMapper() {
|
||||
@@ -580,46 +608,46 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
}
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, state));
|
||||
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode =
|
||||
authorization.getToken(OAuth2AuthorizationCode.class);
|
||||
List<SqlParameterValue> authorizationCodeSqlParameters = toSqlParameterList(
|
||||
"authorization_code_value", "authorization_code_metadata", authorizationCode);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
List<SqlParameterValue> authorizationCodeSqlParameters = toSqlParameterList("authorization_code_value",
|
||||
"authorization_code_metadata", authorizationCode);
|
||||
parameters.addAll(authorizationCodeSqlParameters);
|
||||
|
||||
OAuth2Authorization.Token<OAuth2AccessToken> accessToken =
|
||||
authorization.getToken(OAuth2AccessToken.class);
|
||||
List<SqlParameterValue> accessTokenSqlParameters = toSqlParameterList(
|
||||
"access_token_value", "access_token_metadata", accessToken);
|
||||
OAuth2Authorization.Token<OAuth2AccessToken> accessToken = authorization.getToken(OAuth2AccessToken.class);
|
||||
List<SqlParameterValue> accessTokenSqlParameters = toSqlParameterList("access_token_value",
|
||||
"access_token_metadata", accessToken);
|
||||
parameters.addAll(accessTokenSqlParameters);
|
||||
String accessTokenType = null;
|
||||
String accessTokenScopes = null;
|
||||
if (accessToken != null) {
|
||||
accessTokenType = accessToken.getToken().getTokenType().getValue();
|
||||
if (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) {
|
||||
accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), ",");
|
||||
accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(),
|
||||
",");
|
||||
}
|
||||
}
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType));
|
||||
parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes));
|
||||
|
||||
OAuth2Authorization.Token<OidcIdToken> oidcIdToken = authorization.getToken(OidcIdToken.class);
|
||||
List<SqlParameterValue> oidcIdTokenSqlParameters = toSqlParameterList(
|
||||
"oidc_id_token_value", "oidc_id_token_metadata", oidcIdToken);
|
||||
List<SqlParameterValue> oidcIdTokenSqlParameters = toSqlParameterList("oidc_id_token_value",
|
||||
"oidc_id_token_metadata", oidcIdToken);
|
||||
parameters.addAll(oidcIdTokenSqlParameters);
|
||||
|
||||
OAuth2Authorization.Token<OAuth2RefreshToken> refreshToken = authorization.getRefreshToken();
|
||||
List<SqlParameterValue> refreshTokenSqlParameters = toSqlParameterList(
|
||||
"refresh_token_value", "refresh_token_metadata", refreshToken);
|
||||
List<SqlParameterValue> refreshTokenSqlParameters = toSqlParameterList("refresh_token_value",
|
||||
"refresh_token_metadata", refreshToken);
|
||||
parameters.addAll(refreshTokenSqlParameters);
|
||||
|
||||
OAuth2Authorization.Token<OAuth2UserCode> userCode = authorization.getToken(OAuth2UserCode.class);
|
||||
List<SqlParameterValue> userCodeSqlParameters = toSqlParameterList(
|
||||
"user_code_value", "user_code_metadata", userCode);
|
||||
List<SqlParameterValue> userCodeSqlParameters = toSqlParameterList("user_code_value", "user_code_metadata",
|
||||
userCode);
|
||||
parameters.addAll(userCodeSqlParameters);
|
||||
|
||||
OAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);
|
||||
List<SqlParameterValue> deviceCodeSqlParameters = toSqlParameterList(
|
||||
"device_code_value", "device_code_metadata", deviceCode);
|
||||
List<SqlParameterValue> deviceCodeSqlParameters = toSqlParameterList("device_code_value",
|
||||
"device_code_metadata", deviceCode);
|
||||
parameters.addAll(deviceCodeSqlParameters);
|
||||
|
||||
return parameters;
|
||||
@@ -634,8 +662,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return this.objectMapper;
|
||||
}
|
||||
|
||||
private <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(
|
||||
String tokenColumnName, String tokenMetadataColumnName, OAuth2Authorization.Token<T> token) {
|
||||
private <T extends OAuth2Token> List<SqlParameterValue> toSqlParameterList(String tokenColumnName,
|
||||
String tokenMetadataColumnName, OAuth2Authorization.Token<T> token) {
|
||||
|
||||
List<SqlParameterValue> parameters = new ArrayList<>();
|
||||
String tokenValue = null;
|
||||
@@ -663,7 +691,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -671,6 +700,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
}
|
||||
|
||||
private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
|
||||
|
||||
private final LobCreator lobCreator;
|
||||
|
||||
private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {
|
||||
@@ -707,7 +737,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
}
|
||||
|
||||
private static final class ColumnMetadata {
|
||||
|
||||
private final String columnName;
|
||||
|
||||
private final int dataType;
|
||||
|
||||
private ColumnMetadata(String columnName, int dataType) {
|
||||
@@ -757,7 +789,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
|
||||
}
|
||||
|
||||
private static ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName, int defaultDataType) {
|
||||
private static ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName,
|
||||
int defaultDataType) {
|
||||
Integer dataType = jdbcOperations.execute((ConnectionCallback<Integer>) conn -> {
|
||||
DatabaseMetaData databaseMetaData = conn.getMetaData();
|
||||
ResultSet rs = databaseMetaData.getColumns(null, null, TABLE_NAME, columnName);
|
||||
@@ -765,10 +798,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
return rs.getInt("DATA_TYPE");
|
||||
}
|
||||
// NOTE: (Applies to HSQL)
|
||||
// When a database object is created with one of the CREATE statements or renamed with the ALTER statement,
|
||||
// if the name is enclosed in double quotes, the exact name is used as the case-normal form.
|
||||
// When a database object is created with one of the CREATE statements or
|
||||
// renamed with the ALTER statement,
|
||||
// if the name is enclosed in double quotes, the exact name is used as the
|
||||
// case-normal form.
|
||||
// But if it is not enclosed in double quotes,
|
||||
// the name is converted to uppercase and this uppercase version is stored in the database as the case-normal form.
|
||||
// the name is converted to uppercase and this uppercase version is stored in
|
||||
// the database as the case-normal form.
|
||||
rs = databaseMetaData.getColumns(null, null, TABLE_NAME.toUpperCase(), columnName.toUpperCase());
|
||||
if (rs.next()) {
|
||||
return rs.getInt("DATA_TYPE");
|
||||
@@ -780,9 +816,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic
|
||||
|
||||
private static SqlParameterValue mapToSqlParameter(String columnName, String value) {
|
||||
ColumnMetadata columnMetadata = columnMetadataMap.get(columnName);
|
||||
return Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value) ?
|
||||
new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8)) :
|
||||
new SqlParameterValue(columnMetadata.getDataType(), value);
|
||||
return Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value)
|
||||
? new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8))
|
||||
: new SqlParameterValue(columnMetadata.getDataType(), value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,9 +38,10 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A representation of an OAuth 2.0 Authorization, which holds state related to the authorization granted
|
||||
* to a {@link #getRegisteredClientId() client}, by the {@link #getPrincipalName() resource owner}
|
||||
* or itself in the case of the {@code client_credentials} grant type.
|
||||
* A representation of an OAuth 2.0 Authorization, which holds state related to the
|
||||
* authorization granted to a {@link #getRegisteredClientId() client}, by the
|
||||
* {@link #getPrincipalName() resource owner} or itself in the case of the
|
||||
* {@code client_credentials} grant type.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Krisztian Toth
|
||||
@@ -52,13 +53,21 @@ import org.springframework.util.StringUtils;
|
||||
* @see OAuth2RefreshToken
|
||||
*/
|
||||
public class OAuth2Authorization implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String id;
|
||||
|
||||
private String registeredClientId;
|
||||
|
||||
private String principalName;
|
||||
|
||||
private AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
private Set<String> authorizedScopes;
|
||||
|
||||
private Map<Class<? extends OAuth2Token>, Token<?>> tokens;
|
||||
|
||||
private Map<String, Object> attributes;
|
||||
|
||||
protected OAuth2Authorization() {
|
||||
@@ -66,7 +75,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the identifier for the authorization.
|
||||
*
|
||||
* @return the identifier for the authorization
|
||||
*/
|
||||
public String getId() {
|
||||
@@ -75,7 +83,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the identifier for the {@link RegisteredClient#getId() registered client}.
|
||||
*
|
||||
* @return the {@link RegisteredClient#getId()}
|
||||
*/
|
||||
public String getRegisteredClientId() {
|
||||
@@ -84,7 +91,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@code Principal} name of the resource owner (or client).
|
||||
*
|
||||
* @return the {@code Principal} name of the resource owner (or client)
|
||||
*/
|
||||
public String getPrincipalName() {
|
||||
@@ -92,8 +98,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationGrantType authorization grant type} used for the authorization.
|
||||
*
|
||||
* Returns the {@link AuthorizationGrantType authorization grant type} used for the
|
||||
* authorization.
|
||||
* @return the {@link AuthorizationGrantType} used for the authorization
|
||||
*/
|
||||
public AuthorizationGrantType getAuthorizationGrantType() {
|
||||
@@ -102,7 +108,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the authorized scope(s).
|
||||
*
|
||||
* @return the {@code Set} of authorized scope(s)
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -112,7 +117,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link Token} of type {@link OAuth2AccessToken}.
|
||||
*
|
||||
* @return the {@link Token} of type {@link OAuth2AccessToken}
|
||||
*/
|
||||
public Token<OAuth2AccessToken> getAccessToken() {
|
||||
@@ -121,8 +125,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link Token} of type {@link OAuth2RefreshToken}.
|
||||
*
|
||||
* @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if not available
|
||||
* @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if
|
||||
* not available
|
||||
*/
|
||||
@Nullable
|
||||
public Token<OAuth2RefreshToken> getRefreshToken() {
|
||||
@@ -131,7 +135,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link Token} of type {@code tokenType}.
|
||||
*
|
||||
* @param tokenType the token type
|
||||
* @param <T> the type of the token
|
||||
* @return the {@link Token}, or {@code null} if not available
|
||||
@@ -146,7 +149,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link Token} matching the {@code tokenValue}.
|
||||
*
|
||||
* @param tokenValue the token value
|
||||
* @param <T> the type of the token
|
||||
* @return the {@link Token}, or {@code null} if not available
|
||||
@@ -165,7 +167,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the attribute(s) associated to the authorization.
|
||||
*
|
||||
* @return a {@code Map} of the attribute(s)
|
||||
*/
|
||||
public Map<String, Object> getAttributes() {
|
||||
@@ -174,10 +175,10 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the value of an attribute associated to the authorization.
|
||||
*
|
||||
* @param name the name of the attribute
|
||||
* @param <T> the type of the attribute
|
||||
* @return the value of an attribute associated to the authorization, or {@code null} if not available
|
||||
* @return the value of an attribute associated to the authorization, or {@code null}
|
||||
* if not available
|
||||
*/
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -195,24 +196,22 @@ public class OAuth2Authorization implements Serializable {
|
||||
return false;
|
||||
}
|
||||
OAuth2Authorization that = (OAuth2Authorization) obj;
|
||||
return Objects.equals(this.id, that.id) &&
|
||||
Objects.equals(this.registeredClientId, that.registeredClientId) &&
|
||||
Objects.equals(this.principalName, that.principalName) &&
|
||||
Objects.equals(this.authorizationGrantType, that.authorizationGrantType) &&
|
||||
Objects.equals(this.authorizedScopes, that.authorizedScopes) &&
|
||||
Objects.equals(this.tokens, that.tokens) &&
|
||||
Objects.equals(this.attributes, that.attributes);
|
||||
return Objects.equals(this.id, that.id) && Objects.equals(this.registeredClientId, that.registeredClientId)
|
||||
&& Objects.equals(this.principalName, that.principalName)
|
||||
&& Objects.equals(this.authorizationGrantType, that.authorizationGrantType)
|
||||
&& Objects.equals(this.authorizedScopes, that.authorizedScopes)
|
||||
&& Objects.equals(this.tokens, that.tokens) && Objects.equals(this.attributes, that.attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id, this.registeredClientId, this.principalName,
|
||||
this.authorizationGrantType, this.authorizedScopes, this.tokens, this.attributes);
|
||||
return Objects.hash(this.id, this.registeredClientId, this.principalName, this.authorizationGrantType,
|
||||
this.authorizedScopes, this.tokens, this.attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the provided {@link RegisteredClient#getId()}.
|
||||
*
|
||||
* Returns a new {@link Builder}, initialized with the provided
|
||||
* {@link RegisteredClient#getId()}.
|
||||
* @param registeredClient the {@link RegisteredClient}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -222,20 +221,20 @@ public class OAuth2Authorization implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2Authorization}.
|
||||
*
|
||||
* @param authorization the {@code OAuth2Authorization} used for initializing the {@link Builder}
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided
|
||||
* {@code OAuth2Authorization}.
|
||||
* @param authorization the {@code OAuth2Authorization} used for initializing the
|
||||
* {@link Builder}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder from(OAuth2Authorization authorization) {
|
||||
Assert.notNull(authorization, "authorization cannot be null");
|
||||
return new Builder(authorization.getRegisteredClientId())
|
||||
.id(authorization.getId())
|
||||
.principalName(authorization.getPrincipalName())
|
||||
.authorizationGrantType(authorization.getAuthorizationGrantType())
|
||||
.authorizedScopes(authorization.getAuthorizedScopes())
|
||||
.tokens(authorization.tokens)
|
||||
.attributes(attrs -> attrs.putAll(authorization.getAttributes()));
|
||||
return new Builder(authorization.getRegisteredClientId()).id(authorization.getId())
|
||||
.principalName(authorization.getPrincipalName())
|
||||
.authorizationGrantType(authorization.getAuthorizationGrantType())
|
||||
.authorizedScopes(authorization.getAuthorizedScopes())
|
||||
.tokens(authorization.tokens)
|
||||
.attributes(attrs -> attrs.putAll(authorization.getAttributes()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,7 +244,9 @@ public class OAuth2Authorization implements Serializable {
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public static class Token<T extends OAuth2Token> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
protected static final String TOKEN_METADATA_NAMESPACE = "metadata.token.";
|
||||
|
||||
/**
|
||||
@@ -259,6 +260,7 @@ public class OAuth2Authorization implements Serializable {
|
||||
public static final String CLAIMS_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("claims");
|
||||
|
||||
private final T token;
|
||||
|
||||
private final Map<String, Object> metadata;
|
||||
|
||||
protected Token(T token) {
|
||||
@@ -272,7 +274,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the token of type {@link OAuth2Token}.
|
||||
*
|
||||
* @return the token of type {@link OAuth2Token}
|
||||
*/
|
||||
public T getToken() {
|
||||
@@ -280,9 +281,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the token has been invalidated (e.g. revoked).
|
||||
* The default is {@code false}.
|
||||
*
|
||||
* Returns {@code true} if the token has been invalidated (e.g. revoked). The
|
||||
* default is {@code false}.
|
||||
* @return {@code true} if the token has been invalidated, {@code false} otherwise
|
||||
*/
|
||||
public boolean isInvalidated() {
|
||||
@@ -291,7 +291,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the token has expired.
|
||||
*
|
||||
* @return {@code true} if the token has expired, {@code false} otherwise
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
@@ -300,8 +299,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the token is before the time it can be used.
|
||||
*
|
||||
* @return {@code true} if the token is before the time it can be used, {@code false} otherwise
|
||||
* @return {@code true} if the token is before the time it can be used,
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
public boolean isBeforeUse() {
|
||||
Instant notBefore = null;
|
||||
@@ -313,7 +312,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the token is currently active.
|
||||
*
|
||||
* @return {@code true} if the token is currently active, {@code false} otherwise
|
||||
*/
|
||||
public boolean isActive() {
|
||||
@@ -322,7 +320,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the claims associated to the token.
|
||||
*
|
||||
* @return a {@code Map} of the claims, or {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
@@ -332,7 +329,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the value of the metadata associated to the token.
|
||||
*
|
||||
* @param name the name of the metadata
|
||||
* @param <V> the value type of the metadata
|
||||
* @return the value of the metadata, or {@code null} if not available
|
||||
@@ -346,7 +342,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the metadata associated to the token.
|
||||
*
|
||||
* @return a {@code Map} of the metadata
|
||||
*/
|
||||
public Map<String, Object> getMetadata() {
|
||||
@@ -368,27 +363,35 @@ public class OAuth2Authorization implements Serializable {
|
||||
return false;
|
||||
}
|
||||
Token<?> that = (Token<?>) obj;
|
||||
return Objects.equals(this.token, that.token) &&
|
||||
Objects.equals(this.metadata, that.metadata);
|
||||
return Objects.equals(this.token, that.token) && Objects.equals(this.metadata, that.metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.token, this.metadata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for {@link OAuth2Authorization}.
|
||||
*/
|
||||
public static class Builder implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String id;
|
||||
|
||||
private final String registeredClientId;
|
||||
|
||||
private String principalName;
|
||||
|
||||
private AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
private Set<String> authorizedScopes;
|
||||
|
||||
private Map<Class<? extends OAuth2Token>, Token<?>> tokens = new HashMap<>();
|
||||
|
||||
private final Map<String, Object> attributes = new HashMap<>();
|
||||
|
||||
protected Builder(String registeredClientId) {
|
||||
@@ -397,7 +400,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the identifier for the authorization.
|
||||
*
|
||||
* @param id the identifier for the authorization
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -408,8 +410,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@code Principal} name of the resource owner (or client).
|
||||
*
|
||||
* @param principalName the {@code Principal} name of the resource owner (or client)
|
||||
* @param principalName the {@code Principal} name of the resource owner (or
|
||||
* client)
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder principalName(String principalName) {
|
||||
@@ -418,8 +420,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthorizationGrantType authorization grant type} used for the authorization.
|
||||
*
|
||||
* Sets the {@link AuthorizationGrantType authorization grant type} used for the
|
||||
* authorization.
|
||||
* @param authorizationGrantType the {@link AuthorizationGrantType}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -430,7 +432,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the authorized scope(s).
|
||||
*
|
||||
* @param authorizedScopes the {@code Set} of authorized scope(s)
|
||||
* @return the {@link Builder}
|
||||
* @since 0.4.0
|
||||
@@ -442,7 +443,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2AccessToken access token}.
|
||||
*
|
||||
* @param accessToken the {@link OAuth2AccessToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -452,7 +452,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2RefreshToken refresh token}.
|
||||
*
|
||||
* @param refreshToken the {@link OAuth2RefreshToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -462,25 +461,23 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2Token token}.
|
||||
*
|
||||
* @param token the token
|
||||
* @param <T> the type of the token
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public <T extends OAuth2Token> Builder token(T token) {
|
||||
return token(token, (metadata) -> {});
|
||||
return token(token, (metadata) -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2Token token} and associated metadata.
|
||||
*
|
||||
* @param token the token
|
||||
* @param metadataConsumer a {@code Consumer} of the metadata {@code Map}
|
||||
* @param <T> the type of the token
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public <T extends OAuth2Token> Builder token(T token,
|
||||
Consumer<Map<String, Object>> metadataConsumer) {
|
||||
public <T extends OAuth2Token> Builder token(T token, Consumer<Map<String, Object>> metadataConsumer) {
|
||||
|
||||
Assert.notNull(token, "token cannot be null");
|
||||
Map<String, Object> metadata = Token.defaultMetadata();
|
||||
@@ -501,7 +498,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Adds an attribute associated to the authorization.
|
||||
*
|
||||
* @param name the name of the attribute
|
||||
* @param value the value of the attribute
|
||||
* @return the {@link Builder}
|
||||
@@ -514,9 +510,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the attributes {@code Map}
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* A {@code Consumer} of the attributes {@code Map} allowing the ability to add,
|
||||
* replace, or remove.
|
||||
* @param attributesConsumer a {@link Consumer} of the attributes {@code Map}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -527,7 +522,6 @@ public class OAuth2Authorization implements Serializable {
|
||||
|
||||
/**
|
||||
* Builds a new {@link OAuth2Authorization}.
|
||||
*
|
||||
* @return the {@link OAuth2Authorization}
|
||||
*/
|
||||
public OAuth2Authorization build() {
|
||||
@@ -542,12 +536,8 @@ public class OAuth2Authorization implements Serializable {
|
||||
authorization.registeredClientId = this.registeredClientId;
|
||||
authorization.principalName = this.principalName;
|
||||
authorization.authorizationGrantType = this.authorizationGrantType;
|
||||
authorization.authorizedScopes =
|
||||
Collections.unmodifiableSet(
|
||||
!CollectionUtils.isEmpty(this.authorizedScopes) ?
|
||||
new HashSet<>(this.authorizedScopes) :
|
||||
new HashSet<>()
|
||||
);
|
||||
authorization.authorizedScopes = Collections.unmodifiableSet(!CollectionUtils.isEmpty(this.authorizedScopes)
|
||||
? new HashSet<>(this.authorizedScopes) : new HashSet<>());
|
||||
authorization.tokens = Collections.unmodifiableMap(this.tokens);
|
||||
authorization.attributes = Collections.unmodifiableMap(this.attributes);
|
||||
return authorization;
|
||||
|
||||
@@ -20,13 +20,14 @@ import java.time.Instant;
|
||||
import org.springframework.security.oauth2.core.AbstractOAuth2Token;
|
||||
|
||||
/**
|
||||
* An implementation of an {@link AbstractOAuth2Token}
|
||||
* representing an OAuth 2.0 Authorization Code Grant.
|
||||
* An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0
|
||||
* Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.0.3
|
||||
* @see AbstractOAuth2Token
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1">Section
|
||||
* 4.1 Authorization Code Grant</a>
|
||||
*/
|
||||
public class OAuth2AuthorizationCode extends AbstractOAuth2Token {
|
||||
|
||||
|
||||
@@ -31,26 +31,33 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* A representation of an OAuth 2.0 "consent" to an Authorization request, which holds state related to the
|
||||
* set of {@link #getAuthorities() authorities} granted to a {@link #getRegisteredClientId() client} by the
|
||||
* {@link #getPrincipalName() resource owner}.
|
||||
* A representation of an OAuth 2.0 "consent" to an Authorization request, which holds
|
||||
* state related to the set of {@link #getAuthorities() authorities} granted to a
|
||||
* {@link #getRegisteredClientId() client} by the {@link #getPrincipalName() resource
|
||||
* owner}.
|
||||
* <p>
|
||||
* When authorizing access for a given client, the resource owner may only grant a subset of the authorities
|
||||
* the client requested. The typical use-case is the {@code authorization_code} flow, in which the client
|
||||
* requests a set of {@code scope}s. The resource owner then selects which scopes they grant to the client.
|
||||
* When authorizing access for a given client, the resource owner may only grant a subset
|
||||
* of the authorities the client requested. The typical use-case is the
|
||||
* {@code authorization_code} flow, in which the client requests a set of {@code scope}s.
|
||||
* The resource owner then selects which scopes they grant to the client.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.2
|
||||
*/
|
||||
public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private static final String AUTHORITIES_SCOPE_PREFIX = "SCOPE_";
|
||||
|
||||
private final String registeredClientId;
|
||||
|
||||
private final String principalName;
|
||||
|
||||
private final Set<GrantedAuthority> authorities;
|
||||
|
||||
private OAuth2AuthorizationConsent(String registeredClientId, String principalName, Set<GrantedAuthority> authorities) {
|
||||
private OAuth2AuthorizationConsent(String registeredClientId, String principalName,
|
||||
Set<GrantedAuthority> authorities) {
|
||||
this.registeredClientId = registeredClientId;
|
||||
this.principalName = principalName;
|
||||
this.authorities = Collections.unmodifiableSet(authorities);
|
||||
@@ -58,7 +65,6 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the identifier for the {@link RegisteredClient#getId() registered client}.
|
||||
*
|
||||
* @return the {@link RegisteredClient#getId()}
|
||||
*/
|
||||
public String getRegisteredClientId() {
|
||||
@@ -67,7 +73,6 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@code Principal} name of the resource owner (or client).
|
||||
*
|
||||
* @return the {@code Principal} name of the resource owner (or client)
|
||||
*/
|
||||
public String getPrincipalName() {
|
||||
@@ -75,18 +80,18 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link GrantedAuthority authorities} granted to the client by the principal.
|
||||
*
|
||||
* @return the {@link GrantedAuthority authorities} granted to the client by the principal.
|
||||
* Returns the {@link GrantedAuthority authorities} granted to the client by the
|
||||
* principal.
|
||||
* @return the {@link GrantedAuthority authorities} granted to the client by the
|
||||
* principal.
|
||||
*/
|
||||
public Set<GrantedAuthority> getAuthorities() {
|
||||
return this.authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for obtaining the {@code scope}s granted to the client by the principal,
|
||||
* extracted from the {@link #getAuthorities() authorities}.
|
||||
*
|
||||
* Convenience method for obtaining the {@code scope}s granted to the client by the
|
||||
* principal, extracted from the {@link #getAuthorities() authorities}.
|
||||
* @return the {@code scope}s granted to the client by the principal.
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
@@ -108,9 +113,9 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
return false;
|
||||
}
|
||||
OAuth2AuthorizationConsent that = (OAuth2AuthorizationConsent) obj;
|
||||
return Objects.equals(this.registeredClientId, that.registeredClientId) &&
|
||||
Objects.equals(this.principalName, that.principalName) &&
|
||||
Objects.equals(this.authorities, that.authorities);
|
||||
return Objects.equals(this.registeredClientId, that.registeredClientId)
|
||||
&& Objects.equals(this.principalName, that.principalName)
|
||||
&& Objects.equals(this.authorities, that.authorities);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,26 +124,24 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2AuthorizationConsent}.
|
||||
*
|
||||
* @param authorizationConsent the {@code OAuth2AuthorizationConsent} used for initializing the {@link Builder}
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided
|
||||
* {@code OAuth2AuthorizationConsent}.
|
||||
* @param authorizationConsent the {@code OAuth2AuthorizationConsent} used for
|
||||
* initializing the {@link Builder}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder from(OAuth2AuthorizationConsent authorizationConsent) {
|
||||
Assert.notNull(authorizationConsent, "authorizationConsent cannot be null");
|
||||
return new Builder(
|
||||
authorizationConsent.getRegisteredClientId(),
|
||||
authorizationConsent.getPrincipalName(),
|
||||
authorizationConsent.getAuthorities()
|
||||
);
|
||||
return new Builder(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName(),
|
||||
authorizationConsent.getAuthorities());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the given {@link RegisteredClient#getClientId() registeredClientId}
|
||||
* and {@code Principal} name.
|
||||
*
|
||||
* Returns a new {@link Builder}, initialized with the given
|
||||
* {@link RegisteredClient#getClientId() registeredClientId} and {@code Principal}
|
||||
* name.
|
||||
* @param registeredClientId the {@link RegisteredClient#getId()}
|
||||
* @param principalName the {@code Principal} name
|
||||
* @param principalName the {@code Principal} name
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder withId(@NonNull String registeredClientId, @NonNull String principalName) {
|
||||
@@ -147,15 +150,17 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
return new Builder(registeredClientId, principalName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A builder for {@link OAuth2AuthorizationConsent}.
|
||||
*/
|
||||
public static final class Builder implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String registeredClientId;
|
||||
|
||||
private final String principalName;
|
||||
|
||||
private final Set<GrantedAuthority> authorities = new HashSet<>();
|
||||
|
||||
private Builder(String registeredClientId, String principalName) {
|
||||
@@ -171,10 +176,10 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a scope to the collection of {@code authorities} in the resulting {@link OAuth2AuthorizationConsent},
|
||||
* wrapping it in a {@link SimpleGrantedAuthority}, prefixed by {@code SCOPE_}. For example, a
|
||||
* Adds a scope to the collection of {@code authorities} in the resulting
|
||||
* {@link OAuth2AuthorizationConsent}, wrapping it in a
|
||||
* {@link SimpleGrantedAuthority}, prefixed by {@code SCOPE_}. For example, a
|
||||
* {@code message.write} scope would be stored as {@code SCOPE_message.write}.
|
||||
*
|
||||
* @param scope the scope
|
||||
* @return the {@code Builder} for further configuration
|
||||
*/
|
||||
@@ -186,7 +191,6 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
/**
|
||||
* Adds a {@link GrantedAuthority} to the collection of {@code authorities} in the
|
||||
* resulting {@link OAuth2AuthorizationConsent}.
|
||||
*
|
||||
* @param authority the {@link GrantedAuthority}
|
||||
* @return the {@code Builder} for further configuration
|
||||
*/
|
||||
@@ -196,8 +200,8 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the {@code authorities}, allowing the ability to add, replace or remove.
|
||||
*
|
||||
* A {@code Consumer} of the {@code authorities}, allowing the ability to add,
|
||||
* replace or remove.
|
||||
* @param authoritiesConsumer a {@code Consumer} of the {@code authorities}
|
||||
* @return the {@code Builder} for further configuration
|
||||
*/
|
||||
@@ -209,12 +213,13 @@ public final class OAuth2AuthorizationConsent implements Serializable {
|
||||
/**
|
||||
* Validate the authorities and build the {@link OAuth2AuthorizationConsent}.
|
||||
* There must be at least one {@link GrantedAuthority}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthorizationConsent}
|
||||
*/
|
||||
public OAuth2AuthorizationConsent build() {
|
||||
Assert.notEmpty(this.authorities, "authorities cannot be empty");
|
||||
return new OAuth2AuthorizationConsent(this.registeredClientId, this.principalName, this.authorities);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for the management
|
||||
* of {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}.
|
||||
* Implementations of this interface are responsible for the management of
|
||||
* {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.2
|
||||
@@ -32,14 +32,12 @@ public interface OAuth2AuthorizationConsentService {
|
||||
|
||||
/**
|
||||
* Saves the {@link OAuth2AuthorizationConsent}.
|
||||
*
|
||||
* @param authorizationConsent the {@link OAuth2AuthorizationConsent}
|
||||
*/
|
||||
void save(OAuth2AuthorizationConsent authorizationConsent);
|
||||
|
||||
/**
|
||||
* Removes the {@link OAuth2AuthorizationConsent}.
|
||||
*
|
||||
* @param authorizationConsent the {@link OAuth2AuthorizationConsent}
|
||||
*/
|
||||
void remove(OAuth2AuthorizationConsent authorizationConsent);
|
||||
@@ -47,7 +45,6 @@ public interface OAuth2AuthorizationConsentService {
|
||||
/**
|
||||
* Returns the {@link OAuth2AuthorizationConsent} identified by the provided
|
||||
* {@code registeredClientId} and {@code principalName}, or {@code null} if not found.
|
||||
*
|
||||
* @param registeredClientId the identifier for the {@link RegisteredClient}
|
||||
* @param principalName the name of the {@link Principal}
|
||||
* @return the {@link OAuth2AuthorizationConsent} if found, otherwise {@code null}
|
||||
|
||||
@@ -20,16 +20,16 @@ import java.util.Map;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of an OAuth 2.0 Authorization Server Metadata response,
|
||||
* which is returned from an OAuth 2.0 Authorization Server's Metadata Endpoint,
|
||||
* and contains a set of claims about the Authorization Server's configuration.
|
||||
* The claims are defined by the OAuth 2.0 Authorization Server Metadata
|
||||
* specification (RFC 8414).
|
||||
* A representation of an OAuth 2.0 Authorization Server Metadata response, which is
|
||||
* returned from an OAuth 2.0 Authorization Server's Metadata Endpoint, and contains a set
|
||||
* of claims about the Authorization Server's configuration. The claims are defined by the
|
||||
* OAuth 2.0 Authorization Server Metadata specification (RFC 8414).
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.1
|
||||
* @see AbstractOAuth2AuthorizationServerMetadata
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2. Authorization Server Metadata Response</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2.
|
||||
* Authorization Server Metadata Response</a>
|
||||
*/
|
||||
public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2AuthorizationServerMetadata {
|
||||
|
||||
@@ -39,7 +39,6 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with empty claims.
|
||||
*
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder builder() {
|
||||
@@ -48,14 +47,12 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided claims.
|
||||
*
|
||||
* @param claims the claims to initialize the builder
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder withClaims(Map<String, Object> claims) {
|
||||
Assert.notEmpty(claims, "claims cannot be empty");
|
||||
return new Builder()
|
||||
.claims(c -> c.putAll(claims));
|
||||
return new Builder().claims(c -> c.putAll(claims));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,10 +66,9 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho
|
||||
/**
|
||||
* Validate the claims and build the {@link OAuth2AuthorizationServerMetadata}.
|
||||
* <p>
|
||||
* The following claims are REQUIRED:
|
||||
* {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint}
|
||||
* and {@code response_types_supported}.
|
||||
*
|
||||
* The following claims are REQUIRED: {@code issuer},
|
||||
* {@code authorization_endpoint}, {@code token_endpoint} and
|
||||
* {@code response_types_supported}.
|
||||
* @return the {@link OAuth2AuthorizationServerMetadata}
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -21,22 +21,27 @@ import java.util.List;
|
||||
import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||
|
||||
/**
|
||||
* A {@link ClaimAccessor} for the "claims" an Authorization Server describes about its configuration,
|
||||
* used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
|
||||
* A {@link ClaimAccessor} for the "claims" an Authorization Server describes about its
|
||||
* configuration, used in OAuth 2.0 Authorization Server Metadata and OpenID Connect
|
||||
* Discovery 1.0.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.1
|
||||
* @see ClaimAccessor
|
||||
* @see OAuth2AuthorizationServerMetadataClaimNames
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2.
|
||||
* Authorization Server Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
|
||||
* Provider Metadata</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
|
||||
* Device Authorization Grant Metadata</a>
|
||||
*/
|
||||
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} the Authorization Server asserts as its Issuer Identifier {@code (issuer)}.
|
||||
*
|
||||
* Returns the {@code URL} the Authorization Server asserts as its Issuer Identifier
|
||||
* {@code (issuer)}.
|
||||
* @return the {@code URL} the Authorization Server asserts as its Issuer Identifier
|
||||
*/
|
||||
default URL getIssuer() {
|
||||
@@ -44,8 +49,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Authorization Endpoint {@code (authorization_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OAuth 2.0 Authorization Endpoint
|
||||
* {@code (authorization_endpoint)}.
|
||||
* @return the {@code URL} of the OAuth 2.0 Authorization Endpoint
|
||||
*/
|
||||
default URL getAuthorizationEndpoint() {
|
||||
@@ -53,8 +58,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint {@code (device_authorization_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
|
||||
* {@code (device_authorization_endpoint)}.
|
||||
* @return the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -64,7 +69,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}.
|
||||
*
|
||||
* @return the {@code URL} of the OAuth 2.0 Token Endpoint
|
||||
*/
|
||||
default URL getTokenEndpoint() {
|
||||
@@ -72,8 +76,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token Endpoint {@code (token_endpoint_auth_methods_supported)}.
|
||||
*
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token Endpoint
|
||||
* {@code (token_endpoint_auth_methods_supported)}.
|
||||
* @return the client authentication methods supported by the OAuth 2.0 Token Endpoint
|
||||
*/
|
||||
default List<String> getTokenEndpointAuthenticationMethods() {
|
||||
@@ -82,7 +86,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the JSON Web Key Set {@code (jwks_uri)}.
|
||||
*
|
||||
* @return the {@code URL} of the JSON Web Key Set
|
||||
*/
|
||||
default URL getJwkSetUrl() {
|
||||
@@ -91,7 +94,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code scope} values supported {@code (scopes_supported)}.
|
||||
*
|
||||
* @return the OAuth 2.0 {@code scope} values supported
|
||||
*/
|
||||
default List<String> getScopes() {
|
||||
@@ -99,8 +101,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code response_type} values supported {@code (response_types_supported)}.
|
||||
*
|
||||
* Returns the OAuth 2.0 {@code response_type} values supported
|
||||
* {@code (response_types_supported)}.
|
||||
* @return the OAuth 2.0 {@code response_type} values supported
|
||||
*/
|
||||
default List<String> getResponseTypes() {
|
||||
@@ -108,8 +110,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code grant_type} values supported {@code (grant_types_supported)}.
|
||||
*
|
||||
* Returns the OAuth 2.0 {@code grant_type} values supported
|
||||
* {@code (grant_types_supported)}.
|
||||
* @return the OAuth 2.0 {@code grant_type} values supported
|
||||
*/
|
||||
default List<String> getGrantTypes() {
|
||||
@@ -117,8 +119,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Token Revocation Endpoint {@code (revocation_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OAuth 2.0 Token Revocation Endpoint
|
||||
* {@code (revocation_endpoint)}.
|
||||
* @return the {@code URL} of the OAuth 2.0 Token Revocation Endpoint
|
||||
*/
|
||||
default URL getTokenRevocationEndpoint() {
|
||||
@@ -126,17 +128,19 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint {@code (revocation_endpoint_auth_methods_supported)}.
|
||||
*
|
||||
* @return the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token
|
||||
* Revocation Endpoint {@code (revocation_endpoint_auth_methods_supported)}.
|
||||
* @return the client authentication methods supported by the OAuth 2.0 Token
|
||||
* Revocation Endpoint
|
||||
*/
|
||||
default List<String> getTokenRevocationEndpointAuthenticationMethods() {
|
||||
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED);
|
||||
return getClaimAsStringList(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Token Introspection Endpoint {@code (introspection_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OAuth 2.0 Token Introspection Endpoint
|
||||
* {@code (introspection_endpoint)}.
|
||||
* @return the {@code URL} of the OAuth 2.0 Token Introspection Endpoint
|
||||
*/
|
||||
default URL getTokenIntrospectionEndpoint() {
|
||||
@@ -144,17 +148,19 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint {@code (introspection_endpoint_auth_methods_supported)}.
|
||||
*
|
||||
* @return the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint
|
||||
* Returns the client authentication methods supported by the OAuth 2.0 Token
|
||||
* Introspection Endpoint {@code (introspection_endpoint_auth_methods_supported)}.
|
||||
* @return the client authentication methods supported by the OAuth 2.0 Token
|
||||
* Introspection Endpoint
|
||||
*/
|
||||
default List<String> getTokenIntrospectionEndpointAuthenticationMethods() {
|
||||
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED);
|
||||
return getClaimAsStringList(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint {@code (registration_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint
|
||||
* {@code (registration_endpoint)}.
|
||||
* @return the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -163,8 +169,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported {@code (code_challenge_methods_supported)}.
|
||||
*
|
||||
* Returns the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values
|
||||
* supported {@code (code_challenge_methods_supported)}.
|
||||
* @return the {@code code_challenge_method} values supported
|
||||
*/
|
||||
default List<String> getCodeChallengeMethods() {
|
||||
|
||||
@@ -21,24 +21,31 @@ package org.springframework.security.oauth2.server.authorization;
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.1
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2. Authorization Server Metadata</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4. Device Authorization Grant Metadata</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-2">2.
|
||||
* Authorization Server Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
|
||||
* Provider Metadata</a>
|
||||
* @see <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc8628.html#section-4">4.
|
||||
* Device Authorization Grant Metadata</a>
|
||||
*/
|
||||
public class OAuth2AuthorizationServerMetadataClaimNames {
|
||||
|
||||
/**
|
||||
* {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer Identifier
|
||||
* {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer
|
||||
* Identifier
|
||||
*/
|
||||
public static final String ISSUER = "issuer";
|
||||
|
||||
/**
|
||||
* {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization Endpoint
|
||||
* {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization
|
||||
* Endpoint
|
||||
*/
|
||||
public static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint";
|
||||
|
||||
/**
|
||||
* {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device Authorization Endpoint
|
||||
* {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device
|
||||
* Authorization Endpoint
|
||||
* @since 1.1
|
||||
*/
|
||||
public static final String DEVICE_AUTHORIZATION_ENDPOINT = "device_authorization_endpoint";
|
||||
@@ -49,7 +56,8 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
|
||||
public static final String TOKEN_ENDPOINT = "token_endpoint";
|
||||
|
||||
/**
|
||||
* {@code token_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Endpoint
|
||||
* {@code token_endpoint_auth_methods_supported} - the client authentication methods
|
||||
* supported by the OAuth 2.0 Token Endpoint
|
||||
*/
|
||||
public static final String TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED = "token_endpoint_auth_methods_supported";
|
||||
|
||||
@@ -64,7 +72,8 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
|
||||
public static final String SCOPES_SUPPORTED = "scopes_supported";
|
||||
|
||||
/**
|
||||
* {@code response_types_supported} - the OAuth 2.0 {@code response_type} values supported
|
||||
* {@code response_types_supported} - the OAuth 2.0 {@code response_type} values
|
||||
* supported
|
||||
*/
|
||||
public static final String RESPONSE_TYPES_SUPPORTED = "response_types_supported";
|
||||
|
||||
@@ -74,33 +83,39 @@ public class OAuth2AuthorizationServerMetadataClaimNames {
|
||||
public static final String GRANT_TYPES_SUPPORTED = "grant_types_supported";
|
||||
|
||||
/**
|
||||
* {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation Endpoint
|
||||
* {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation
|
||||
* Endpoint
|
||||
*/
|
||||
public static final String REVOCATION_ENDPOINT = "revocation_endpoint";
|
||||
|
||||
/**
|
||||
* {@code revocation_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
* {@code revocation_endpoint_auth_methods_supported} - the client authentication
|
||||
* methods supported by the OAuth 2.0 Token Revocation Endpoint
|
||||
*/
|
||||
public static final String REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED = "revocation_endpoint_auth_methods_supported";
|
||||
|
||||
/**
|
||||
* {@code introspection_endpoint} - the {@code URL} of the OAuth 2.0 Token Introspection Endpoint
|
||||
* {@code introspection_endpoint} - the {@code URL} of the OAuth 2.0 Token
|
||||
* Introspection Endpoint
|
||||
*/
|
||||
public static final String INTROSPECTION_ENDPOINT = "introspection_endpoint";
|
||||
|
||||
/**
|
||||
* {@code introspection_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint
|
||||
* {@code introspection_endpoint_auth_methods_supported} - the client authentication
|
||||
* methods supported by the OAuth 2.0 Token Introspection Endpoint
|
||||
*/
|
||||
public static final String INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED = "introspection_endpoint_auth_methods_supported";
|
||||
|
||||
/**
|
||||
* {@code registration_endpoint} - the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint
|
||||
* {@code registration_endpoint} - the {@code URL} of the OAuth 2.0 Dynamic Client
|
||||
* Registration Endpoint
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public static final String REGISTRATION_ENDPOINT = "registration_endpoint";
|
||||
|
||||
/**
|
||||
* {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported
|
||||
* {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE)
|
||||
* {@code code_challenge_method} values supported
|
||||
*/
|
||||
public static final String CODE_CHALLENGE_METHODS_SUPPORTED = "code_challenge_methods_supported";
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ package org.springframework.security.oauth2.server.authorization;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Implementations of this interface are responsible for the management
|
||||
* of {@link OAuth2Authorization OAuth 2.0 Authorization(s)}.
|
||||
* Implementations of this interface are responsible for the management of
|
||||
* {@link OAuth2Authorization OAuth 2.0 Authorization(s)}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.0.1
|
||||
@@ -30,22 +30,19 @@ public interface OAuth2AuthorizationService {
|
||||
|
||||
/**
|
||||
* Saves the {@link OAuth2Authorization}.
|
||||
*
|
||||
* @param authorization the {@link OAuth2Authorization}
|
||||
*/
|
||||
void save(OAuth2Authorization authorization);
|
||||
|
||||
/**
|
||||
* Removes the {@link OAuth2Authorization}.
|
||||
*
|
||||
* @param authorization the {@link OAuth2Authorization}
|
||||
*/
|
||||
void remove(OAuth2Authorization authorization);
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2Authorization} identified by the provided {@code id},
|
||||
* or {@code null} if not found.
|
||||
*
|
||||
* Returns the {@link OAuth2Authorization} identified by the provided {@code id}, or
|
||||
* {@code null} if not found.
|
||||
* @param id the authorization identifier
|
||||
* @return the {@link OAuth2Authorization} if found, otherwise {@code null}
|
||||
*/
|
||||
@@ -53,9 +50,8 @@ public interface OAuth2AuthorizationService {
|
||||
OAuth2Authorization findById(String id);
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2Authorization} containing the provided {@code token},
|
||||
* or {@code null} if not found.
|
||||
*
|
||||
* Returns the {@link OAuth2Authorization} containing the provided {@code token}, or
|
||||
* {@code null} if not found.
|
||||
* @param token the token credential
|
||||
* @param tokenType the {@link OAuth2TokenType token type}
|
||||
* @return the {@link OAuth2Authorization} if found, otherwise {@code null}
|
||||
|
||||
@@ -39,10 +39,13 @@ import org.springframework.util.Assert;
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.1
|
||||
* @see OAuth2TokenIntrospectionClaimAccessor
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.2">Section 2.2 Introspection Response</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.2">Section
|
||||
* 2.2 Introspection Response</a>
|
||||
*/
|
||||
public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionClaimAccessor, Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Map<String, Object> claims;
|
||||
|
||||
private OAuth2TokenIntrospection(Map<String, Object> claims) {
|
||||
@@ -51,7 +54,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Returns the claims in the Token Introspection Response.
|
||||
*
|
||||
* @return a {@code Map} of the claims
|
||||
*/
|
||||
@Override
|
||||
@@ -60,8 +62,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} initialized with the {@link #isActive() active} claim to {@code false}.
|
||||
*
|
||||
* Constructs a new {@link Builder} initialized with the {@link #isActive() active}
|
||||
* claim to {@code false}.
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder builder() {
|
||||
@@ -69,9 +71,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} initialized with the provided {@link #isActive() active} claim.
|
||||
*
|
||||
* @param active {@code true} if the token is currently active, {@code false} otherwise
|
||||
* Constructs a new {@link Builder} initialized with the provided {@link #isActive()
|
||||
* active} claim.
|
||||
* @param active {@code true} if the token is currently active, {@code false}
|
||||
* otherwise
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder builder(boolean active) {
|
||||
@@ -80,7 +83,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} initialized with the provided claims.
|
||||
*
|
||||
* @param claims the claims to initialize the builder
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -93,6 +95,7 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
* A builder for {@link OAuth2TokenIntrospection}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Map<String, Object> claims = new LinkedHashMap<>();
|
||||
|
||||
private Builder(boolean active) {
|
||||
@@ -100,9 +103,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicator of whether or not the presented token is currently active, REQUIRED.
|
||||
*
|
||||
* @param active {@code true} if the token is currently active, {@code false} otherwise
|
||||
* Sets the indicator of whether or not the presented token is currently active,
|
||||
* REQUIRED.
|
||||
* @param active {@code true} if the token is currently active, {@code false}
|
||||
* otherwise
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder active(boolean active) {
|
||||
@@ -111,7 +115,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Add the scope associated with this token, OPTIONAL.
|
||||
*
|
||||
* @param scope the scope associated with this token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -121,10 +124,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the scope(s) associated with this token,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param scopesConsumer a {@code Consumer} of the scope(s) associated with this token
|
||||
* A {@code Consumer} of the scope(s) associated with this token, allowing the
|
||||
* ability to add, replace, or remove, OPTIONAL.
|
||||
* @param scopesConsumer a {@code Consumer} of the scope(s) associated with this
|
||||
* token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder scopes(Consumer<List<String>> scopesConsumer) {
|
||||
@@ -133,9 +136,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the client identifier for the OAuth 2.0 client that requested this token, OPTIONAL.
|
||||
*
|
||||
* @param clientId the client identifier for the OAuth 2.0 client that requested this token
|
||||
* Sets the client identifier for the OAuth 2.0 client that requested this token,
|
||||
* OPTIONAL.
|
||||
* @param clientId the client identifier for the OAuth 2.0 client that requested
|
||||
* this token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder clientId(String clientId) {
|
||||
@@ -143,9 +147,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the human-readable identifier for the resource owner who authorized this token, OPTIONAL.
|
||||
*
|
||||
* @param username the human-readable identifier for the resource owner who authorized this token
|
||||
* Sets the human-readable identifier for the resource owner who authorized this
|
||||
* token, OPTIONAL.
|
||||
* @param username the human-readable identifier for the resource owner who
|
||||
* authorized this token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder username(String username) {
|
||||
@@ -154,7 +159,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the token type (e.g. bearer), OPTIONAL.
|
||||
*
|
||||
* @param tokenType the token type
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -164,7 +168,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the time indicating when this token will expire, OPTIONAL.
|
||||
*
|
||||
* @param expiresAt the time indicating when this token will expire
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -174,7 +177,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the time indicating when this token was originally issued, OPTIONAL.
|
||||
*
|
||||
* @param issuedAt the time indicating when this token was originally issued
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -184,7 +186,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the time indicating when this token is not to be used before, OPTIONAL.
|
||||
*
|
||||
* @param notBefore the time indicating when this token is not to be used before
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -193,9 +194,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subject of the token, usually a machine-readable identifier
|
||||
* of the resource owner who authorized this token, OPTIONAL.
|
||||
*
|
||||
* Sets the subject of the token, usually a machine-readable identifier of the
|
||||
* resource owner who authorized this token, OPTIONAL.
|
||||
* @param subject the subject of the token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -205,8 +205,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Add the identifier representing the intended audience for this token, OPTIONAL.
|
||||
*
|
||||
* @param audience the identifier representing the intended audience for this token
|
||||
* @param audience the identifier representing the intended audience for this
|
||||
* token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder audience(String audience) {
|
||||
@@ -215,10 +215,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the intended audience(s) for this token,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param audiencesConsumer a {@code Consumer} of the intended audience(s) for this token
|
||||
* A {@code Consumer} of the intended audience(s) for this token, allowing the
|
||||
* ability to add, replace, or remove, OPTIONAL.
|
||||
* @param audiencesConsumer a {@code Consumer} of the intended audience(s) for
|
||||
* this token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder audiences(Consumer<List<String>> audiencesConsumer) {
|
||||
@@ -228,7 +228,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the issuer of this token, OPTIONAL.
|
||||
*
|
||||
* @param issuer the issuer of this token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -238,7 +237,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the identifier for the token, OPTIONAL.
|
||||
*
|
||||
* @param jti the identifier for the token
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -248,7 +246,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
/**
|
||||
* Sets the claim.
|
||||
*
|
||||
* @param name the claim name
|
||||
* @param value the claim value
|
||||
* @return the {@link Builder} for further configuration
|
||||
@@ -263,7 +260,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
/**
|
||||
* Provides access to every {@link #claim(String, Object)} declared so far with
|
||||
* the possibility to add, replace, or remove.
|
||||
*
|
||||
* @param claimsConsumer a {@code Consumer} of the claims
|
||||
* @return the {@link Builder} for further configurations
|
||||
*/
|
||||
@@ -276,7 +272,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
* Validate the claims and build the {@link OAuth2TokenIntrospection}.
|
||||
* <p>
|
||||
* The following claims are REQUIRED: {@code active}
|
||||
*
|
||||
* @return the {@link OAuth2TokenIntrospection}
|
||||
*/
|
||||
public OAuth2TokenIntrospection build() {
|
||||
@@ -286,21 +281,27 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
private void validate() {
|
||||
Assert.notNull(this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), "active cannot be null");
|
||||
Assert.isInstanceOf(Boolean.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), "active must be of type boolean");
|
||||
Assert.isInstanceOf(Boolean.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE),
|
||||
"active must be of type boolean");
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.SCOPE)) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE), "scope must be of type List");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE),
|
||||
"scope must be of type List");
|
||||
}
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.EXP)) {
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.EXP), "exp must be of type Instant");
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.EXP),
|
||||
"exp must be of type Instant");
|
||||
}
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.IAT)) {
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.IAT), "iat must be of type Instant");
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.IAT),
|
||||
"iat must be of type Instant");
|
||||
}
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.NBF)) {
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.NBF), "nbf must be of type Instant");
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.NBF),
|
||||
"nbf must be of type Instant");
|
||||
}
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.AUD)) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.AUD), "aud must be of type List");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.AUD),
|
||||
"aud must be of type List");
|
||||
}
|
||||
if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.ISS)) {
|
||||
validateURL(this.claims.get(OAuth2TokenIntrospectionClaimNames.ISS), "iss must be a valid URL");
|
||||
@@ -331,9 +332,12 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC
|
||||
|
||||
try {
|
||||
new URI(url.toString()).toURL();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(errorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,17 +25,21 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.0.1
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-4.1.2">4.1.2 OAuth Token Type Hints Registry</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-4.1.2">4.1.2
|
||||
* OAuth Token Type Hints Registry</a>
|
||||
*/
|
||||
public final class OAuth2TokenType implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
public static final OAuth2TokenType ACCESS_TOKEN = new OAuth2TokenType("access_token");
|
||||
|
||||
public static final OAuth2TokenType REFRESH_TOKEN = new OAuth2TokenType("refresh_token");
|
||||
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenType} using the provided value.
|
||||
*
|
||||
* @param value the value of the token type
|
||||
*/
|
||||
public OAuth2TokenType(String value) {
|
||||
@@ -45,7 +49,6 @@ public final class OAuth2TokenType implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the value of the token type.
|
||||
*
|
||||
* @return the value of the token type
|
||||
*/
|
||||
public String getValue() {
|
||||
@@ -68,4 +71,5 @@ public final class OAuth2TokenType implements Serializable {
|
||||
public int hashCode() {
|
||||
return getValue().hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,8 +37,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client Authentication,
|
||||
* which authenticates the {@link OAuth2ParameterNames#CLIENT_SECRET client_secret} parameter.
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client
|
||||
* Authentication, which authenticates the {@link OAuth2ParameterNames#CLIENT_SECRET
|
||||
* client_secret} parameter.
|
||||
*
|
||||
* @author Patryk Kostrzewa
|
||||
* @author Joe Grandja
|
||||
@@ -50,15 +51,20 @@ import org.springframework.util.Assert;
|
||||
* @see PasswordEncoder
|
||||
*/
|
||||
public final class ClientSecretAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final CodeVerifierAuthenticator codeVerifierAuthenticator;
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* Constructs a {@code ClientSecretAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs a {@code ClientSecretAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
@@ -72,12 +78,12 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PasswordEncoder} used to validate
|
||||
* the {@link RegisteredClient#getClientSecret() client secret}.
|
||||
* If not set, the client secret will be compared using
|
||||
* Sets the {@link PasswordEncoder} used to validate the
|
||||
* {@link RegisteredClient#getClientSecret() client secret}. If not set, the client
|
||||
* secret will be compared using
|
||||
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.
|
||||
*
|
||||
* @param passwordEncoder the {@link PasswordEncoder} used to validate the client secret
|
||||
* @param passwordEncoder the {@link PasswordEncoder} used to validate the client
|
||||
* secret
|
||||
*/
|
||||
public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
|
||||
Assert.notNull(passwordEncoder, "passwordEncoder cannot be null");
|
||||
@@ -86,13 +92,14 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2ClientAuthenticationToken clientAuthentication =
|
||||
(OAuth2ClientAuthenticationToken) authentication;
|
||||
OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;
|
||||
|
||||
// @formatter:off
|
||||
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientAuthentication.getClientAuthenticationMethod()) &&
|
||||
!ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
return null;
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
String clientId = clientAuthentication.getPrincipal().toString();
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
|
||||
@@ -104,8 +111,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
if (!registeredClient.getClientAuthenticationMethods().contains(
|
||||
clientAuthentication.getClientAuthenticationMethod())) {
|
||||
if (!registeredClient.getClientAuthenticationMethods()
|
||||
.contains(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
throwInvalidClient("authentication_method");
|
||||
}
|
||||
|
||||
@@ -122,15 +129,15 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
throwInvalidClient(OAuth2ParameterNames.CLIENT_SECRET);
|
||||
}
|
||||
|
||||
if (registeredClient.getClientSecretExpiresAt() != null &&
|
||||
Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) {
|
||||
if (registeredClient.getClientSecretExpiresAt() != null
|
||||
&& Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) {
|
||||
throwInvalidClient("client_secret_expires_at");
|
||||
}
|
||||
|
||||
if (this.passwordEncoder.upgradeEncoding(registeredClient.getClientSecret())) {
|
||||
registeredClient = RegisteredClient.from(registeredClient)
|
||||
.clientSecret(this.passwordEncoder.encode(clientSecret))
|
||||
.build();
|
||||
.clientSecret(this.passwordEncoder.encode(clientSecret))
|
||||
.build();
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
}
|
||||
|
||||
@@ -138,7 +145,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
this.logger.trace("Validated client authentication parameters");
|
||||
}
|
||||
|
||||
// Validate the "code_verifier" parameter for the confidential client, if available
|
||||
// Validate the "code_verifier" parameter for the confidential client, if
|
||||
// available
|
||||
this.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -155,11 +163,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP
|
||||
}
|
||||
|
||||
private static void throwInvalidClient(String parameterName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName,
|
||||
ERROR_URI
|
||||
);
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName, ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An authenticator used for OAuth 2.0 Client Authentication,
|
||||
* which authenticates the {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter.
|
||||
* An authenticator used for OAuth 2.0 Client Authentication, which authenticates the
|
||||
* {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @author Joe Grandja
|
||||
@@ -50,8 +50,11 @@ import org.springframework.util.StringUtils;
|
||||
* @see OAuth2AuthorizationService
|
||||
*/
|
||||
final class CodeVerifierAuthenticator {
|
||||
|
||||
private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
CodeVerifierAuthenticator(OAuth2AuthorizationService authorizationService) {
|
||||
@@ -59,8 +62,7 @@ final class CodeVerifierAuthenticator {
|
||||
this.authorizationService = authorizationService;
|
||||
}
|
||||
|
||||
void authenticateRequired(OAuth2ClientAuthenticationToken clientAuthentication,
|
||||
RegisteredClient registeredClient) {
|
||||
void authenticateRequired(OAuth2ClientAuthenticationToken clientAuthentication, RegisteredClient registeredClient) {
|
||||
if (!authenticate(clientAuthentication, registeredClient)) {
|
||||
throwInvalidGrant(PkceParameterNames.CODE_VERIFIER);
|
||||
}
|
||||
@@ -79,9 +81,8 @@ final class CodeVerifierAuthenticator {
|
||||
return false;
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
(String) parameters.get(OAuth2ParameterNames.CODE),
|
||||
AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken((String) parameters.get(OAuth2ParameterNames.CODE), AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throwInvalidGrant(OAuth2ParameterNames.CODE);
|
||||
}
|
||||
@@ -90,11 +91,11 @@ final class CodeVerifierAuthenticator {
|
||||
this.logger.trace("Retrieved authorization with authorization code");
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
|
||||
OAuth2AuthorizationRequest.class.getName());
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization
|
||||
.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
|
||||
String codeChallenge = (String) authorizationRequest.getAdditionalParameters()
|
||||
.get(PkceParameterNames.CODE_CHALLENGE);
|
||||
.get(PkceParameterNames.CODE_CHALLENGE);
|
||||
String codeVerifier = (String) parameters.get(PkceParameterNames.CODE_VERIFIER);
|
||||
if (!StringUtils.hasText(codeChallenge)) {
|
||||
if (registeredClient.getClientSettings().isRequireProofKey() ||
|
||||
@@ -104,7 +105,8 @@ final class CodeVerifierAuthenticator {
|
||||
" for registered client '%s'", registeredClient.getId()));
|
||||
}
|
||||
throwInvalidGrant(PkceParameterNames.CODE_CHALLENGE);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not authenticate code verifier since requireProofKey=false");
|
||||
}
|
||||
@@ -117,7 +119,7 @@ final class CodeVerifierAuthenticator {
|
||||
}
|
||||
|
||||
String codeChallengeMethod = (String) authorizationRequest.getAdditionalParameters()
|
||||
.get(PkceParameterNames.CODE_CHALLENGE_METHOD);
|
||||
.get(PkceParameterNames.CODE_CHALLENGE_METHOD);
|
||||
if (!codeVerifierValid(codeVerifier, codeChallenge, codeChallengeMethod)) {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Invalid request: code_verifier is missing or invalid" +
|
||||
@@ -134,22 +136,27 @@ final class CodeVerifierAuthenticator {
|
||||
}
|
||||
|
||||
private static boolean authorizationCodeGrant(Map<String, Object> parameters) {
|
||||
// @formatter:off
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(
|
||||
parameters.get(OAuth2ParameterNames.GRANT_TYPE)) &&
|
||||
parameters.get(OAuth2ParameterNames.CODE) != null;
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private boolean codeVerifierValid(String codeVerifier, String codeChallenge, String codeChallengeMethod) {
|
||||
if (!StringUtils.hasText(codeVerifier)) {
|
||||
return false;
|
||||
} else if ("S256".equals(codeChallengeMethod)) {
|
||||
}
|
||||
else if ("S256".equals(codeChallengeMethod)) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));
|
||||
String encodedVerifier = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
|
||||
return encodedVerifier.equals(codeChallenge);
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
// It is unlikely that SHA-256 is not available on the server. If it is not available,
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
// It is unlikely that SHA-256 is not available on the server. If it is
|
||||
// not available,
|
||||
// there will likely be bigger issues as well. We default to SERVER_ERROR.
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.SERVER_ERROR);
|
||||
}
|
||||
@@ -158,11 +165,8 @@ final class CodeVerifierAuthenticator {
|
||||
}
|
||||
|
||||
private static void throwInvalidGrant(String parameterName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
OAuth2ErrorCodes.INVALID_GRANT,
|
||||
"Client authentication failed: " + parameterName,
|
||||
null
|
||||
);
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT,
|
||||
"Client authentication failed: " + parameterName, null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client Authentication,
|
||||
* which authenticates the {@link Jwt} {@link OAuth2ParameterNames#CLIENT_ASSERTION client_assertion} parameter.
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client
|
||||
* Authentication, which authenticates the {@link Jwt}
|
||||
* {@link OAuth2ParameterNames#CLIENT_ASSERTION client_assertion} parameter.
|
||||
*
|
||||
* @author Rafal Lewczuk
|
||||
* @author Joe Grandja
|
||||
@@ -50,17 +51,23 @@ import org.springframework.util.Assert;
|
||||
* @see JwtClientAssertionDecoderFactory
|
||||
*/
|
||||
public final class JwtClientAssertionAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
|
||||
private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD =
|
||||
new ClientAuthenticationMethod("urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
||||
|
||||
private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod(
|
||||
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final CodeVerifierAuthenticator codeVerifierAuthenticator;
|
||||
|
||||
private JwtDecoderFactory<RegisteredClient> jwtDecoderFactory;
|
||||
|
||||
/**
|
||||
* Constructs a {@code JwtClientAssertionAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs a {@code JwtClientAssertionAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
@@ -75,8 +82,7 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2ClientAuthenticationToken clientAuthentication =
|
||||
(OAuth2ClientAuthenticationToken) authentication;
|
||||
OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;
|
||||
|
||||
if (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
return null;
|
||||
@@ -92,10 +98,12 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
if (!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.PRIVATE_KEY_JWT) &&
|
||||
!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
|
||||
throwInvalidClient("authentication_method");
|
||||
}
|
||||
// @formatter:on
|
||||
|
||||
if (clientAuthentication.getCredentials() == null) {
|
||||
throwInvalidClient("credentials");
|
||||
@@ -105,7 +113,8 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(registeredClient);
|
||||
try {
|
||||
jwtAssertion = jwtDecoder.decode(clientAuthentication.getCredentials().toString());
|
||||
} catch (JwtException ex) {
|
||||
}
|
||||
catch (JwtException ex) {
|
||||
throwInvalidClient(OAuth2ParameterNames.CLIENT_ASSERTION, ex);
|
||||
}
|
||||
|
||||
@@ -113,13 +122,16 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
this.logger.trace("Validated client authentication parameters");
|
||||
}
|
||||
|
||||
// Validate the "code_verifier" parameter for the confidential client, if available
|
||||
// Validate the "code_verifier" parameter for the confidential client, if
|
||||
// available
|
||||
this.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient);
|
||||
|
||||
// @formatter:off
|
||||
ClientAuthenticationMethod clientAuthenticationMethod =
|
||||
registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm() instanceof SignatureAlgorithm ?
|
||||
ClientAuthenticationMethod.PRIVATE_KEY_JWT :
|
||||
ClientAuthenticationMethod.CLIENT_SECRET_JWT;
|
||||
// @formatter:on
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Authenticated client assertion");
|
||||
@@ -134,11 +146,12 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient}
|
||||
* and is used for authenticating a {@link Jwt} Bearer Token during OAuth 2.0 Client Authentication.
|
||||
* The default factory is {@link JwtClientAssertionDecoderFactory}.
|
||||
*
|
||||
* @param jwtDecoderFactory the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient}
|
||||
* Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the
|
||||
* specified {@link RegisteredClient} and is used for authenticating a {@link Jwt}
|
||||
* Bearer Token during OAuth 2.0 Client Authentication. The default factory is
|
||||
* {@link JwtClientAssertionDecoderFactory}.
|
||||
* @param jwtDecoderFactory the {@link JwtDecoderFactory} that provides a
|
||||
* {@link JwtDecoder} for the specified {@link RegisteredClient}
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public void setJwtDecoderFactory(JwtDecoderFactory<RegisteredClient> jwtDecoderFactory) {
|
||||
@@ -151,11 +164,8 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic
|
||||
}
|
||||
|
||||
private static void throwInvalidClient(String parameterName, Throwable cause) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName,
|
||||
ERROR_URI
|
||||
);
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName, ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error, error.toString(), cause);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,9 @@ import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient}
|
||||
* and is used for authenticating a {@link Jwt} Bearer Token during OAuth 2.0 Client Authentication.
|
||||
* A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the
|
||||
* specified {@link RegisteredClient} and is used for authenticating a {@link Jwt} Bearer
|
||||
* Token during OAuth 2.0 Client Authentication.
|
||||
*
|
||||
* @author Rafal Lewczuk
|
||||
* @author Joe Grandja
|
||||
@@ -72,13 +73,16 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory<RegisteredClient> {
|
||||
|
||||
/**
|
||||
* The default {@code OAuth2TokenValidator<Jwt>} factory that validates the {@link JwtClaimNames#ISS iss},
|
||||
* {@link JwtClaimNames#SUB sub}, {@link JwtClaimNames#AUD aud}, {@link JwtClaimNames#EXP exp} and
|
||||
* {@link JwtClaimNames#NBF nbf} claims of the {@link Jwt} for the specified {@link RegisteredClient}.
|
||||
* The default {@code OAuth2TokenValidator<Jwt>} factory that validates the
|
||||
* {@link JwtClaimNames#ISS iss}, {@link JwtClaimNames#SUB sub},
|
||||
* {@link JwtClaimNames#AUD aud}, {@link JwtClaimNames#EXP exp} and
|
||||
* {@link JwtClaimNames#NBF nbf} claims of the {@link Jwt} for the specified
|
||||
* {@link RegisteredClient}.
|
||||
*/
|
||||
public static final Function<RegisteredClient, OAuth2TokenValidator<Jwt>> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory();
|
||||
|
||||
private static final String JWT_CLIENT_AUTHENTICATION_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7523#section-3";
|
||||
|
||||
private static final Map<JwsAlgorithm, String> JCA_ALGORITHM_MAPPINGS;
|
||||
|
||||
static {
|
||||
@@ -99,6 +103,7 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
}
|
||||
|
||||
private final Map<String, JwtDecoder> jwtDecoders = new ConcurrentHashMap<>();
|
||||
|
||||
private Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = DEFAULT_JWT_VALIDATOR_FACTORY;
|
||||
|
||||
@Override
|
||||
@@ -112,11 +117,12 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the factory that provides an {@link OAuth2TokenValidator}
|
||||
* for the specified {@link RegisteredClient} and is used by the {@link JwtDecoder}.
|
||||
* The default {@code OAuth2TokenValidator<Jwt>} factory is {@link #DEFAULT_JWT_VALIDATOR_FACTORY}.
|
||||
*
|
||||
* @param jwtValidatorFactory the factory that provides an {@link OAuth2TokenValidator} for the specified {@link RegisteredClient}
|
||||
* Sets the factory that provides an {@link OAuth2TokenValidator} for the specified
|
||||
* {@link RegisteredClient} and is used by the {@link JwtDecoder}. The default
|
||||
* {@code OAuth2TokenValidator<Jwt>} factory is
|
||||
* {@link #DEFAULT_JWT_VALIDATOR_FACTORY}.
|
||||
* @param jwtValidatorFactory the factory that provides an
|
||||
* {@link OAuth2TokenValidator} for the specified {@link RegisteredClient}
|
||||
*/
|
||||
public void setJwtValidatorFactory(Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
|
||||
Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null");
|
||||
@@ -124,26 +130,27 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
}
|
||||
|
||||
private static NimbusJwtDecoder buildDecoder(RegisteredClient registeredClient) {
|
||||
JwsAlgorithm jwsAlgorithm = registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm();
|
||||
JwsAlgorithm jwsAlgorithm = registeredClient.getClientSettings()
|
||||
.getTokenEndpointAuthenticationSigningAlgorithm();
|
||||
if (jwsAlgorithm instanceof SignatureAlgorithm) {
|
||||
String jwkSetUrl = registeredClient.getClientSettings().getJwkSetUrl();
|
||||
if (!StringUtils.hasText(jwkSetUrl)) {
|
||||
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Failed to find a Signature Verifier for Client: '"
|
||||
+ registeredClient.getId()
|
||||
"Failed to find a Signature Verifier for Client: '" + registeredClient.getId()
|
||||
+ "'. Check to ensure you have configured the JWK Set URL.",
|
||||
JWT_CLIENT_AUTHENTICATION_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(oauth2Error);
|
||||
}
|
||||
return NimbusJwtDecoder.withJwkSetUri(jwkSetUrl).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)
|
||||
.restOperations(restTemplate).build();
|
||||
return NimbusJwtDecoder.withJwkSetUri(jwkSetUrl)
|
||||
.jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm)
|
||||
.restOperations(restTemplate)
|
||||
.build();
|
||||
}
|
||||
if (jwsAlgorithm instanceof MacAlgorithm) {
|
||||
String clientSecret = registeredClient.getClientSecret();
|
||||
if (!StringUtils.hasText(clientSecret)) {
|
||||
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Failed to find a Signature Verifier for Client: '"
|
||||
+ registeredClient.getId()
|
||||
"Failed to find a Signature Verifier for Client: '" + registeredClient.getId()
|
||||
+ "'. Check to ensure you have configured the client secret.",
|
||||
JWT_CLIENT_AUTHENTICATION_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(oauth2Error);
|
||||
@@ -153,8 +160,7 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
return NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build();
|
||||
}
|
||||
OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Failed to find a Signature Verifier for Client: '"
|
||||
+ registeredClient.getId()
|
||||
"Failed to find a Signature Verifier for Client: '" + registeredClient.getId()
|
||||
+ "'. Check to ensure you have configured a valid JWS Algorithm: '" + jwsAlgorithm + "'.",
|
||||
JWT_CLIENT_AUTHENTICATION_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(oauth2Error);
|
||||
@@ -163,13 +169,10 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
private static Function<RegisteredClient, OAuth2TokenValidator<Jwt>> defaultJwtValidatorFactory() {
|
||||
return (registeredClient) -> {
|
||||
String clientId = registeredClient.getClientId();
|
||||
return new DelegatingOAuth2TokenValidator<>(
|
||||
new JwtClaimValidator<>(JwtClaimNames.ISS, clientId::equals),
|
||||
return new DelegatingOAuth2TokenValidator<>(new JwtClaimValidator<>(JwtClaimNames.ISS, clientId::equals),
|
||||
new JwtClaimValidator<>(JwtClaimNames.SUB, clientId::equals),
|
||||
new JwtClaimValidator<>(JwtClaimNames.AUD, containsAudience()),
|
||||
new JwtClaimValidator<>(JwtClaimNames.EXP, Objects::nonNull),
|
||||
new JwtTimestampValidator()
|
||||
);
|
||||
new JwtClaimValidator<>(JwtClaimNames.EXP, Objects::nonNull), new JwtTimestampValidator());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -194,12 +197,15 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings();
|
||||
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext
|
||||
.getAuthorizationServerSettings();
|
||||
List<String> audience = new ArrayList<>();
|
||||
audience.add(authorizationServerContext.getIssuer());
|
||||
audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenEndpoint()));
|
||||
audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenIntrospectionEndpoint()));
|
||||
audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenRevocationEndpoint()));
|
||||
audience.add(asUrl(authorizationServerContext.getIssuer(),
|
||||
authorizationServerSettings.getTokenIntrospectionEndpoint()));
|
||||
audience.add(asUrl(authorizationServerContext.getIssuer(),
|
||||
authorizationServerSettings.getTokenRevocationEndpoint()));
|
||||
return audience;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used when issuing an
|
||||
* OAuth 2.0 Access Token and (optional) Refresh Token.
|
||||
* An {@link Authentication} implementation used when issuing an OAuth 2.0 Access Token
|
||||
* and (optional) Refresh Token.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Madhu Bhat
|
||||
@@ -41,28 +41,34 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2ClientAuthenticationToken
|
||||
*/
|
||||
public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final RegisteredClient registeredClient;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final OAuth2AccessToken accessToken;
|
||||
|
||||
private final OAuth2RefreshToken refreshToken;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param registeredClient the registered client
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param accessToken the access token
|
||||
*/
|
||||
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient,
|
||||
Authentication clientPrincipal, OAuth2AccessToken accessToken) {
|
||||
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,
|
||||
OAuth2AccessToken accessToken) {
|
||||
this(registeredClient, clientPrincipal, accessToken, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param registeredClient the registered client
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param accessToken the access token
|
||||
@@ -74,8 +80,8 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param registeredClient the registered client
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param accessToken the access token
|
||||
@@ -83,7 +89,8 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
* @param additionalParameters the additional parameters
|
||||
*/
|
||||
public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal,
|
||||
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken, Map<String, Object> additionalParameters) {
|
||||
OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken,
|
||||
Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
@@ -108,7 +115,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
|
||||
/**
|
||||
* Returns the {@link RegisteredClient registered client}.
|
||||
*
|
||||
* @return the {@link RegisteredClient}
|
||||
*/
|
||||
public RegisteredClient getRegisteredClient() {
|
||||
@@ -117,7 +123,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AccessToken access token}.
|
||||
*
|
||||
* @return the {@link OAuth2AccessToken}
|
||||
*/
|
||||
public OAuth2AccessToken getAccessToken() {
|
||||
@@ -126,7 +131,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2RefreshToken refresh token}.
|
||||
*
|
||||
* @return the {@link OAuth2RefreshToken} or {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
@@ -136,10 +140,10 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return a {@code Map} of the additional parameters, may be empty
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
return this.additionalParameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ public interface OAuth2AuthenticationContext extends Context {
|
||||
|
||||
/**
|
||||
* Returns the {@link Authentication} associated to the context.
|
||||
*
|
||||
* @param <T> the type of the {@code Authentication}
|
||||
* @return the {@link Authentication}
|
||||
*/
|
||||
@@ -53,6 +52,7 @@ public interface OAuth2AuthenticationContext extends Context {
|
||||
* @since 0.2.1
|
||||
*/
|
||||
abstract class AbstractBuilder<T extends OAuth2AuthenticationContext, B extends AbstractBuilder<T, B>> {
|
||||
|
||||
private final Map<Object, Object> context = new HashMap<>();
|
||||
|
||||
protected AbstractBuilder(Authentication authentication) {
|
||||
@@ -62,7 +62,6 @@ public interface OAuth2AuthenticationContext extends Context {
|
||||
|
||||
/**
|
||||
* Associates an attribute.
|
||||
*
|
||||
* @param key the key for the attribute
|
||||
* @param value the value of the attribute
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
@@ -75,9 +74,8 @@ public interface OAuth2AuthenticationContext extends Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the attributes {@code Map}
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* A {@code Consumer} of the attributes {@code Map} allowing the ability to add,
|
||||
* replace, or remove.
|
||||
* @param contextConsumer a {@link Consumer} of the attributes {@code Map}
|
||||
* @return the {@link AbstractBuilder} for further configuration
|
||||
*/
|
||||
@@ -102,7 +100,6 @@ public interface OAuth2AuthenticationContext extends Context {
|
||||
|
||||
/**
|
||||
* Builds a new {@link OAuth2AuthenticationContext}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthenticationContext}
|
||||
*/
|
||||
public abstract T build();
|
||||
|
||||
@@ -46,8 +46,7 @@ final class OAuth2AuthenticationProviderUtils {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
|
||||
static <T extends OAuth2Token> OAuth2Authorization invalidate(
|
||||
OAuth2Authorization authorization, T token) {
|
||||
static <T extends OAuth2Token> OAuth2Authorization invalidate(OAuth2Authorization authorization, T token) {
|
||||
|
||||
// @formatter:off
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization)
|
||||
@@ -74,4 +73,5 @@ final class OAuth2AuthenticationProviderUtils {
|
||||
|
||||
return authorizationBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,7 +66,8 @@ import org.springframework.util.StringUtils;
|
||||
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code Grant.
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code
|
||||
* Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Daniel Garnier-Moiroux
|
||||
@@ -76,23 +77,32 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationProvider
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2TokenGenerator
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.1">Section 4.1 Authorization Code Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3">Section 4.1.3 Access Token Request</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1">Section 4.1 Authorization
|
||||
* Code Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3">Section 4.1.3 Access
|
||||
* Token Request</a>
|
||||
*/
|
||||
public final class OAuth2AuthorizationCodeAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||
private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE =
|
||||
new OAuth2TokenType(OAuth2ParameterNames.CODE);
|
||||
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE =
|
||||
new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||
|
||||
private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE);
|
||||
|
||||
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
private SessionRegistry sessionRegistry;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
@@ -107,19 +117,18 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication =
|
||||
(OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||
OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(authorizationCodeAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
authorizationCodeAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
@@ -128,40 +137,45 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
this.logger.trace("Retrieved authorization with authorization code");
|
||||
}
|
||||
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode =
|
||||
authorization.getToken(OAuth2AuthorizationCode.class);
|
||||
OAuth2Authorization.Token<OAuth2AuthorizationCode> authorizationCode = authorization
|
||||
.getToken(OAuth2AuthorizationCode.class);
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(
|
||||
OAuth2AuthorizationRequest.class.getName());
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization
|
||||
.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
|
||||
if (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) {
|
||||
if (!authorizationCode.isInvalidated()) {
|
||||
// Invalidate the authorization code given that a different client is attempting to use it
|
||||
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, authorizationCode.getToken());
|
||||
// Invalidate the authorization code given that a different client is
|
||||
// attempting to use it
|
||||
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization,
|
||||
authorizationCode.getToken());
|
||||
this.authorizationService.save(authorization);
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(LogMessage.format("Invalidated authorization code used by registered client '%s'", registeredClient.getId()));
|
||||
this.logger.warn(LogMessage.format("Invalidated authorization code used by registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
}
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(authorizationRequest.getRedirectUri()) &&
|
||||
!authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) {
|
||||
if (StringUtils.hasText(authorizationRequest.getRedirectUri())
|
||||
&& !authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
|
||||
if (!authorizationCode.isActive()) {
|
||||
if (authorizationCode.isInvalidated()) {
|
||||
OAuth2Authorization.Token<? extends OAuth2Token> token = authorization.getRefreshToken() != null ?
|
||||
authorization.getRefreshToken() :
|
||||
authorization.getAccessToken();
|
||||
OAuth2Authorization.Token<? extends OAuth2Token> token = authorization.getRefreshToken() != null
|
||||
? authorization.getRefreshToken() : authorization.getAccessToken();
|
||||
if (token != null) {
|
||||
// Invalidate the access (and refresh) token as the client is attempting to use the authorization code more than once
|
||||
// Invalidate the access (and refresh) token as the client is
|
||||
// attempting to use the authorization code more than once
|
||||
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, token.getToken());
|
||||
this.authorizationService.save(authorization);
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(LogMessage.format("Invalidated authorization token(s) previously issued to registered client '%s'", registeredClient.getId()));
|
||||
this.logger.warn(LogMessage.format(
|
||||
"Invalidated authorization token(s) previously issued to registered client '%s'",
|
||||
registeredClient.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,14 +218,17 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
// Do not issue refresh token to public client
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
@@ -240,7 +257,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
// Compute (and use) hash for Session ID
|
||||
sessionInformation = new SessionInformation(sessionInformation.getPrincipal(),
|
||||
createHash(sessionInformation.getSessionId()), sessionInformation.getLastRequest());
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"Failed to compute hash for Session ID.", ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
@@ -266,9 +284,10 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
|
||||
idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),
|
||||
generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());
|
||||
authorizationBuilder.token(idToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(idToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
|
||||
}
|
||||
else {
|
||||
idToken = null;
|
||||
}
|
||||
|
||||
@@ -293,8 +312,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
this.logger.trace("Authenticated token request");
|
||||
}
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(
|
||||
registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken,
|
||||
additionalParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -304,8 +323,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth
|
||||
|
||||
/**
|
||||
* Sets the {@link SessionRegistry} used to track OpenID Connect sessions.
|
||||
*
|
||||
* @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect sessions
|
||||
* @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect
|
||||
* sessions
|
||||
* @since 1.1
|
||||
*/
|
||||
public void setSessionRegistry(SessionRegistry sessionRegistry) {
|
||||
|
||||
@@ -23,7 +23,8 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used for the OAuth 2.0 Authorization Code Grant.
|
||||
* An {@link Authentication} implementation used for the OAuth 2.0 Authorization Code
|
||||
* Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Madhu Bhat
|
||||
@@ -33,12 +34,14 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationCodeAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
|
||||
|
||||
private final String code;
|
||||
|
||||
private final String redirectUri;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param code the authorization code
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param redirectUri the redirect uri
|
||||
@@ -54,7 +57,6 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat
|
||||
|
||||
/**
|
||||
* Returns the authorization code.
|
||||
*
|
||||
* @return the authorization code
|
||||
*/
|
||||
public String getCode() {
|
||||
@@ -63,11 +65,11 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat
|
||||
|
||||
/**
|
||||
* Returns the redirect uri.
|
||||
*
|
||||
* @return the redirect uri
|
||||
*/
|
||||
@Nullable
|
||||
public String getRedirectUri() {
|
||||
return this.redirectUri;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,18 +37,19 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
|
||||
* @see OAuth2AuthorizationConsentAuthenticationProvider
|
||||
*/
|
||||
final class OAuth2AuthorizationCodeGenerator implements OAuth2TokenGenerator<OAuth2AuthorizationCode> {
|
||||
private final StringKeyGenerator authorizationCodeGenerator =
|
||||
new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
private final StringKeyGenerator authorizationCodeGenerator = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public OAuth2AuthorizationCode generate(OAuth2TokenContext context) {
|
||||
if (context.getTokenType() == null ||
|
||||
!OAuth2ParameterNames.CODE.equals(context.getTokenType().getValue())) {
|
||||
if (context.getTokenType() == null || !OAuth2ParameterNames.CODE.equals(context.getTokenType().getValue())) {
|
||||
return null;
|
||||
}
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getAuthorizationCodeTimeToLive());
|
||||
Instant expiresAt = issuedAt
|
||||
.plus(context.getRegisteredClient().getTokenSettings().getAuthorizationCodeTimeToLive());
|
||||
return new OAuth2AuthorizationCode(this.authorizationCodeGenerator.generateKey(), issuedAt, expiresAt);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,10 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information
|
||||
* and is used when validating the OAuth 2.0 Authorization Request used in the Authorization Code Grant.
|
||||
* An {@link OAuth2AuthenticationContext} that holds an
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information
|
||||
* and is used when validating the OAuth 2.0 Authorization Request used in the
|
||||
* Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.4.0
|
||||
@@ -35,6 +37,7 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)
|
||||
*/
|
||||
public final class OAuth2AuthorizationCodeRequestAuthenticationContext implements OAuth2AuthenticationContext {
|
||||
|
||||
private final Map<Object, Object> context;
|
||||
|
||||
private OAuth2AuthorizationCodeRequestAuthenticationContext(Map<Object, Object> context) {
|
||||
@@ -56,7 +59,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
|
||||
|
||||
/**
|
||||
* Returns the {@link RegisteredClient registered client}.
|
||||
*
|
||||
* @return the {@link RegisteredClient}
|
||||
*/
|
||||
public RegisteredClient getRegisteredClient() {
|
||||
@@ -64,8 +66,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
|
||||
*
|
||||
* Constructs a new {@link Builder} with the provided
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
|
||||
* @param authentication the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -76,7 +78,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
|
||||
/**
|
||||
* A builder for {@link OAuth2AuthorizationCodeRequestAuthenticationContext}.
|
||||
*/
|
||||
public static final class Builder extends AbstractBuilder<OAuth2AuthorizationCodeRequestAuthenticationContext, Builder> {
|
||||
public static final class Builder
|
||||
extends AbstractBuilder<OAuth2AuthorizationCodeRequestAuthenticationContext, Builder> {
|
||||
|
||||
private Builder(OAuth2AuthorizationCodeRequestAuthenticationToken authentication) {
|
||||
super(authentication);
|
||||
@@ -84,7 +87,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
|
||||
|
||||
/**
|
||||
* Sets the {@link RegisteredClient registered client}.
|
||||
*
|
||||
* @param registeredClient the {@link RegisteredClient}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -94,7 +96,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement
|
||||
|
||||
/**
|
||||
* Builds a new {@link OAuth2AuthorizationCodeRequestAuthenticationContext}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthorizationCodeRequestAuthenticationContext}
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationContext build() {
|
||||
|
||||
@@ -21,8 +21,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
|
||||
/**
|
||||
* This exception is thrown by {@link OAuth2AuthorizationCodeRequestAuthenticationProvider}
|
||||
* when an attempt to authenticate the OAuth 2.0 Authorization Request (or Consent) fails.
|
||||
* This exception is thrown by
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationProvider} when an attempt to
|
||||
* authenticate the OAuth 2.0 Authorization Request (or Consent) fails.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.2
|
||||
@@ -30,13 +31,15 @@ import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth2AuthenticationException {
|
||||
|
||||
private final OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using
|
||||
* the provided parameters.
|
||||
* @param error the {@link OAuth2Error OAuth 2.0 Error}
|
||||
* @param authorizationCodeRequestAuthentication the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent)
|
||||
* @param authorizationCodeRequestAuthentication the {@link Authentication} instance
|
||||
* of the OAuth 2.0 Authorization Request (or Consent)
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error,
|
||||
@Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {
|
||||
@@ -45,11 +48,12 @@ public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using
|
||||
* the provided parameters.
|
||||
* @param error the {@link OAuth2Error OAuth 2.0 Error}
|
||||
* @param cause the root cause
|
||||
* @param authorizationCodeRequestAuthentication the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent)
|
||||
* @param authorizationCodeRequestAuthentication the {@link Authentication} instance
|
||||
* of the OAuth 2.0 Authorization Request (or Consent)
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error, Throwable cause,
|
||||
@Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {
|
||||
@@ -58,8 +62,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent), or {@code null} if not available.
|
||||
*
|
||||
* Returns the {@link Authentication} instance of the OAuth 2.0 Authorization Request
|
||||
* (or Consent), or {@code null} if not available.
|
||||
* @return the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -52,8 +52,8 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Request
|
||||
* used in the Authorization Code Grant.
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization
|
||||
* Request used in the Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Steve Riesenberg
|
||||
@@ -65,30 +65,41 @@ import org.springframework.util.StringUtils;
|
||||
* @see RegisteredClientRepository
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2AuthorizationConsentService
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1">Section 4.1.1 Authorization Request</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1">Section 4.1.1
|
||||
* Authorization Request</a>
|
||||
*/
|
||||
public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1";
|
||||
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR =
|
||||
new Base64StringKeyGenerator(Base64.getUrlEncoder());
|
||||
|
||||
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder());
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
|
||||
private OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator();
|
||||
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationValidator();
|
||||
|
||||
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = new OAuth2AuthorizationCodeRequestAuthenticationValidator();
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using
|
||||
* the provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param authorizationConsentService the authorization consent service
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null");
|
||||
@@ -99,11 +110,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
(OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication;
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
|
||||
authorizationCodeRequestAuthentication.getClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findByClientId(authorizationCodeRequestAuthentication.getClientId());
|
||||
if (registeredClient == null) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,
|
||||
authorizationCodeRequestAuthentication, null);
|
||||
@@ -113,10 +123,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationContext.with(authorizationCodeRequestAuthentication)
|
||||
.registeredClient(registeredClient)
|
||||
.build();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = OAuth2AuthorizationCodeRequestAuthenticationContext
|
||||
.with(authorizationCodeRequestAuthentication)
|
||||
.registeredClient(registeredClient)
|
||||
.build();
|
||||
this.authenticationValidator.accept(authenticationContext);
|
||||
|
||||
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
|
||||
@@ -125,14 +135,17 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
}
|
||||
|
||||
// code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE)
|
||||
String codeChallenge = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(PkceParameterNames.CODE_CHALLENGE);
|
||||
String codeChallenge = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()
|
||||
.get(PkceParameterNames.CODE_CHALLENGE);
|
||||
if (StringUtils.hasText(codeChallenge)) {
|
||||
String codeChallengeMethod = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(PkceParameterNames.CODE_CHALLENGE_METHOD);
|
||||
String codeChallengeMethod = (String) authorizationCodeRequestAuthentication.getAdditionalParameters()
|
||||
.get(PkceParameterNames.CODE_CHALLENGE_METHOD);
|
||||
if (!StringUtils.hasText(codeChallengeMethod) || !"S256".equals(codeChallengeMethod)) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, PKCE_ERROR_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient, null);
|
||||
}
|
||||
} else if (registeredClient.getClientSettings().isRequireProofKey()) {
|
||||
}
|
||||
else if (registeredClient.getClientSettings().isRequireProofKey()) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient, null);
|
||||
}
|
||||
@@ -155,22 +168,22 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode()
|
||||
.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())
|
||||
.clientId(registeredClient.getClientId())
|
||||
.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())
|
||||
.scopes(authorizationCodeRequestAuthentication.getScopes())
|
||||
.state(authorizationCodeRequestAuthentication.getState())
|
||||
.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())
|
||||
.build();
|
||||
.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())
|
||||
.clientId(registeredClient.getClientId())
|
||||
.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())
|
||||
.scopes(authorizationCodeRequestAuthentication.getScopes())
|
||||
.state(authorizationCodeRequestAuthentication.getState())
|
||||
.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())
|
||||
.build();
|
||||
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
|
||||
registeredClient.getId(), principal.getName());
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
|
||||
.findById(registeredClient.getId(), principal.getName());
|
||||
|
||||
if (requireAuthorizationConsent(registeredClient, authorizationRequest, currentAuthorizationConsent)) {
|
||||
String state = DEFAULT_STATE_GENERATOR.generateKey();
|
||||
OAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest)
|
||||
.attribute(OAuth2ParameterNames.STATE, state)
|
||||
.build();
|
||||
.attribute(OAuth2ParameterNames.STATE, state)
|
||||
.build();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
logger.trace("Generated authorization consent state");
|
||||
@@ -178,8 +191,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null ?
|
||||
currentAuthorizationConsent.getScopes() : null;
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null
|
||||
? currentAuthorizationConsent.getScopes() : null;
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Saved authorization");
|
||||
@@ -189,8 +202,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
registeredClient.getClientId(), principal, state, currentAuthorizedScopes, null);
|
||||
}
|
||||
|
||||
OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(
|
||||
authorizationCodeRequestAuthentication, registeredClient, null, authorizationRequest.getScopes());
|
||||
OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationCodeRequestAuthentication,
|
||||
registeredClient, null, authorizationRequest.getScopes());
|
||||
OAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext);
|
||||
if (authorizationCode == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
@@ -203,9 +216,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest)
|
||||
.authorizedScopes(authorizationRequest.getScopes())
|
||||
.token(authorizationCode)
|
||||
.build();
|
||||
.authorizedScopes(authorizationRequest.getScopes())
|
||||
.token(authorizationCode)
|
||||
.build();
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -232,40 +245,47 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}.
|
||||
*
|
||||
* @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the
|
||||
* {@link OAuth2AuthorizationCode}.
|
||||
* @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates
|
||||
* the {@link OAuth2AuthorizationCode}
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public void setAuthorizationCodeGenerator(OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {
|
||||
public void setAuthorizationCodeGenerator(
|
||||
OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {
|
||||
Assert.notNull(authorizationCodeGenerator, "authorizationCodeGenerator cannot be null");
|
||||
this.authorizationCodeGenerator = authorizationCodeGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext}
|
||||
* and is responsible for validating specific OAuth 2.0 Authorization Request parameters
|
||||
* associated in the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
|
||||
* The default authentication validator is {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}.
|
||||
* Sets the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for
|
||||
* validating specific OAuth 2.0 Authorization Request parameters associated in the
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. The default
|
||||
* authentication validator is
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> The authentication validator MUST throw {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails.
|
||||
*
|
||||
* @param authenticationValidator the {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for validating specific OAuth 2.0 Authorization Request parameters
|
||||
* <b>NOTE:</b> The authentication validator MUST throw
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails.
|
||||
* @param authenticationValidator the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for
|
||||
* validating specific OAuth 2.0 Authorization Request parameters
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public void setAuthenticationValidator(Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {
|
||||
public void setAuthenticationValidator(
|
||||
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {
|
||||
Assert.notNull(authenticationValidator, "authenticationValidator cannot be null");
|
||||
this.authenticationValidator = authenticationValidator;
|
||||
}
|
||||
|
||||
private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient, Authentication principal,
|
||||
OAuth2AuthorizationRequest authorizationRequest) {
|
||||
private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient,
|
||||
Authentication principal, OAuth2AuthorizationRequest authorizationRequest) {
|
||||
return OAuth2Authorization.withRegisteredClient(registeredClient)
|
||||
.principalName(principal.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.attribute(Principal.class.getName(), principal)
|
||||
.attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest);
|
||||
.principalName(principal.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
.attribute(Principal.class.getName(), principal)
|
||||
.attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest);
|
||||
}
|
||||
|
||||
private static OAuth2TokenContext createAuthorizationCodeTokenContext(
|
||||
@@ -297,13 +317,13 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
return false;
|
||||
}
|
||||
// 'openid' scope does not require consent
|
||||
if (authorizationRequest.getScopes().contains(OidcScopes.OPENID) &&
|
||||
authorizationRequest.getScopes().size() == 1) {
|
||||
if (authorizationRequest.getScopes().contains(OidcScopes.OPENID)
|
||||
&& authorizationRequest.getScopes().size() == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authorizationConsent != null &&
|
||||
authorizationConsent.getScopes().containsAll(authorizationRequest.getScopes())) {
|
||||
if (authorizationConsent != null
|
||||
&& authorizationConsent.getScopes().containsAll(authorizationRequest.getScopes())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -311,9 +331,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
}
|
||||
|
||||
private static boolean isPrincipalAuthenticated(Authentication principal) {
|
||||
return principal != null &&
|
||||
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
|
||||
principal.isAuthenticated();
|
||||
return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())
|
||||
&& principal.isAuthenticated();
|
||||
}
|
||||
|
||||
private static void throwError(String errorCode, String parameterName,
|
||||
@@ -326,35 +345,39 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,
|
||||
RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {
|
||||
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
|
||||
throwError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient, authorizationRequest);
|
||||
throwError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient,
|
||||
authorizationRequest);
|
||||
}
|
||||
|
||||
private static void throwError(OAuth2Error error, String parameterName,
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,
|
||||
RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {
|
||||
|
||||
String redirectUri = resolveRedirectUri(authorizationCodeRequestAuthentication, authorizationRequest, registeredClient);
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) &&
|
||||
(parameterName.equals(OAuth2ParameterNames.CLIENT_ID) ||
|
||||
parameterName.equals(OAuth2ParameterNames.STATE))) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
String redirectUri = resolveRedirectUri(authorizationCodeRequestAuthentication, authorizationRequest,
|
||||
registeredClient);
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)
|
||||
&& (parameterName.equals(OAuth2ParameterNames.CLIENT_ID)
|
||||
|| parameterName.equals(OAuth2ParameterNames.STATE))) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
}
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationCodeRequestAuthentication.getClientId(),
|
||||
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,
|
||||
authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),
|
||||
authorizationCodeRequestAuthentication.getAdditionalParameters());
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationCodeRequestAuthentication.getAuthorizationUri(),
|
||||
authorizationCodeRequestAuthentication.getClientId(),
|
||||
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,
|
||||
authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),
|
||||
authorizationCodeRequestAuthentication.getAdditionalParameters());
|
||||
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult);
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error,
|
||||
authorizationCodeRequestAuthenticationResult);
|
||||
}
|
||||
|
||||
private static String resolveRedirectUri(
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,
|
||||
OAuth2AuthorizationRequest authorizationRequest, RegisteredClient registeredClient) {
|
||||
|
||||
if (authorizationCodeRequestAuthentication != null && StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) {
|
||||
if (authorizationCodeRequestAuthentication != null
|
||||
&& StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) {
|
||||
return authorizationCodeRequestAuthentication.getRedirectUri();
|
||||
}
|
||||
if (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) {
|
||||
|
||||
@@ -29,8 +29,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation for the OAuth 2.0 Authorization Request
|
||||
* used in the Authorization Code Grant.
|
||||
* An {@link Authentication} implementation for the OAuth 2.0 Authorization Request used
|
||||
* in the Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.2
|
||||
@@ -38,19 +38,28 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationConsentAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String authorizationUri;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final String redirectUri;
|
||||
|
||||
private final String state;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
private final OAuth2AuthorizationCode authorizationCode;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
@@ -60,8 +69,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
* @param additionalParameters the additional parameters
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, Authentication principal,
|
||||
@Nullable String redirectUri, @Nullable String state, @Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId,
|
||||
Authentication principal, @Nullable String redirectUri, @Nullable String state,
|
||||
@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.hasText(authorizationUri, "authorizationUri cannot be empty");
|
||||
Assert.hasText(clientId, "clientId cannot be empty");
|
||||
@@ -71,20 +81,15 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
this.principal = principal;
|
||||
this.redirectUri = redirectUri;
|
||||
this.state = state;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ?
|
||||
new HashSet<>(scopes) :
|
||||
Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ?
|
||||
new HashMap<>(additionalParameters) :
|
||||
Collections.emptyMap());
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
this.authorizationCode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
@@ -94,8 +99,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
* @param scopes the authorized scope(s)
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, Authentication principal,
|
||||
OAuth2AuthorizationCode authorizationCode, @Nullable String redirectUri, @Nullable String state, @Nullable Set<String> scopes) {
|
||||
public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId,
|
||||
Authentication principal, OAuth2AuthorizationCode authorizationCode, @Nullable String redirectUri,
|
||||
@Nullable String state, @Nullable Set<String> scopes) {
|
||||
super(Collections.emptyList());
|
||||
Assert.hasText(authorizationUri, "authorizationUri cannot be empty");
|
||||
Assert.hasText(clientId, "clientId cannot be empty");
|
||||
@@ -107,10 +113,7 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
this.authorizationCode = authorizationCode;
|
||||
this.redirectUri = redirectUri;
|
||||
this.state = state;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ?
|
||||
new HashSet<>(scopes) :
|
||||
Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.emptyMap();
|
||||
setAuthenticated(true);
|
||||
}
|
||||
@@ -127,7 +130,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the authorization URI.
|
||||
*
|
||||
* @return the authorization URI
|
||||
*/
|
||||
public String getAuthorizationUri() {
|
||||
@@ -136,7 +138,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the client identifier.
|
||||
*
|
||||
* @return the client identifier
|
||||
*/
|
||||
public String getClientId() {
|
||||
@@ -145,7 +146,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the redirect uri.
|
||||
*
|
||||
* @return the redirect uri
|
||||
*/
|
||||
@Nullable
|
||||
@@ -155,7 +155,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the state.
|
||||
*
|
||||
* @return the state
|
||||
*/
|
||||
@Nullable
|
||||
@@ -165,8 +164,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the requested (or authorized) scope(s).
|
||||
*
|
||||
* @return the requested (or authorized) scope(s), or an empty {@code Set} if not available
|
||||
* @return the requested (or authorized) scope(s), or an empty {@code Set} if not
|
||||
* available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
@@ -174,7 +173,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters, or an empty {@code Map} if not available
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
@@ -183,7 +181,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AuthorizationCode}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthorizationCode}
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
@@ -33,15 +33,18 @@ import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext}
|
||||
* containing an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* and is the default {@link OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer) authentication validator}
|
||||
* used for validating specific OAuth 2.0 Authorization Request parameters used in the Authorization Code Grant.
|
||||
* A {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationContext} containing an
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and is the default
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)
|
||||
* authentication validator} used for validating specific OAuth 2.0 Authorization Request
|
||||
* parameters used in the Authorization Code Grant.
|
||||
*
|
||||
* <p>
|
||||
* The default implementation first validates {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}
|
||||
* and then {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}.
|
||||
* If validation fails, an {@link OAuth2AuthorizationCodeRequestAuthenticationException} is thrown.
|
||||
* The default implementation first validates
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()} and then
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. If validation
|
||||
* fails, an {@link OAuth2AuthorizationCodeRequestAuthenticationException} is thrown.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.4.0
|
||||
@@ -49,24 +52,26 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationToken
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer)
|
||||
*/
|
||||
public final class OAuth2AuthorizationCodeRequestAuthenticationValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
|
||||
public final class OAuth2AuthorizationCodeRequestAuthenticationValidator
|
||||
implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
private static final Log LOGGER = LogFactory.getLog(OAuth2AuthorizationCodeRequestAuthenticationValidator.class);
|
||||
|
||||
/**
|
||||
* The default validator for {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}.
|
||||
* The default validator for
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}.
|
||||
*/
|
||||
public static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_SCOPE_VALIDATOR =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationValidator::validateScope;
|
||||
public static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_SCOPE_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateScope;
|
||||
|
||||
/**
|
||||
* The default validator for {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}.
|
||||
* The default validator for
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}.
|
||||
*/
|
||||
public static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_REDIRECT_URI_VALIDATOR =
|
||||
OAuth2AuthorizationCodeRequestAuthenticationValidator::validateRedirectUri;
|
||||
public static final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> DEFAULT_REDIRECT_URI_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateRedirectUri;
|
||||
|
||||
private final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator =
|
||||
DEFAULT_REDIRECT_URI_VALIDATOR.andThen(DEFAULT_SCOPE_VALIDATOR);
|
||||
private final Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator = DEFAULT_REDIRECT_URI_VALIDATOR
|
||||
.andThen(DEFAULT_SCOPE_VALIDATOR);
|
||||
|
||||
@Override
|
||||
public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
|
||||
@@ -74,8 +79,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
}
|
||||
|
||||
private static void validateScope(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationContext.getAuthentication();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext
|
||||
.getAuthentication();
|
||||
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
|
||||
|
||||
Set<String> requestedScopes = authorizationCodeRequestAuthentication.getScopes();
|
||||
@@ -91,8 +96,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
}
|
||||
|
||||
private static void validateRedirectUri(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationContext.getAuthentication();
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext
|
||||
.getAuthentication();
|
||||
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
|
||||
|
||||
String requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();
|
||||
@@ -103,7 +108,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
UriComponents requestedRedirect = null;
|
||||
try {
|
||||
requestedRedirect = UriComponentsBuilder.fromUriString(requestedRedirectUri).build();
|
||||
} catch (Exception ex) { }
|
||||
}
|
||||
catch (Exception ex) {
|
||||
}
|
||||
if (requestedRedirect == null || requestedRedirect.getFragment() != null) {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(LogMessage.format("Invalid request: redirect_uri is missing or contains a fragment" +
|
||||
@@ -114,15 +121,18 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
}
|
||||
|
||||
if (!isLoopbackAddress(requestedRedirect.getHost())) {
|
||||
// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3
|
||||
// As per
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3
|
||||
// When comparing client redirect URIs against pre-registered URIs,
|
||||
// authorization servers MUST utilize exact string matching.
|
||||
if (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient);
|
||||
}
|
||||
} else {
|
||||
// As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2
|
||||
}
|
||||
else {
|
||||
// As per
|
||||
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2
|
||||
// The authorization server MUST allow any port to be specified at the
|
||||
// time of the request for loopback IP redirect URIs, to accommodate
|
||||
// clients that obtain an available ephemeral port from the operating
|
||||
@@ -146,11 +156,12 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// ***** redirect_uri is NOT available in authorization request
|
||||
|
||||
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID) ||
|
||||
registeredClient.getRedirectUris().size() != 1) {
|
||||
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)
|
||||
|| registeredClient.getRedirectUris().size() != 1) {
|
||||
// redirect_uri is REQUIRED for OpenID Connect
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI,
|
||||
authorizationCodeRequestAuthentication, registeredClient);
|
||||
@@ -173,12 +184,13 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
}
|
||||
try {
|
||||
int[] address = new int[ipv4Octets.length];
|
||||
for (int i=0; i < ipv4Octets.length; i++) {
|
||||
for (int i = 0; i < ipv4Octets.length; i++) {
|
||||
address[i] = Integer.parseInt(ipv4Octets[i]);
|
||||
}
|
||||
return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 &&
|
||||
address[2] <= 255 && address[3] >= 1 && address[3] <= 255;
|
||||
} catch (NumberFormatException ex) {
|
||||
return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 && address[2] <= 255
|
||||
&& address[3] >= 1 && address[3] <= 255;
|
||||
}
|
||||
catch (NumberFormatException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -194,23 +206,24 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication,
|
||||
RegisteredClient registeredClient) {
|
||||
|
||||
String redirectUri = StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri()) ?
|
||||
authorizationCodeRequestAuthentication.getRedirectUri() :
|
||||
registeredClient.getRedirectUris().iterator().next();
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) &&
|
||||
parameterName.equals(OAuth2ParameterNames.REDIRECT_URI)) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
String redirectUri = StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())
|
||||
? authorizationCodeRequestAuthentication.getRedirectUri()
|
||||
: registeredClient.getRedirectUris().iterator().next();
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)
|
||||
&& parameterName.equals(OAuth2ParameterNames.REDIRECT_URI)) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
}
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationCodeRequestAuthentication.getClientId(),
|
||||
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,
|
||||
authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),
|
||||
authorizationCodeRequestAuthentication.getAdditionalParameters());
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationCodeRequestAuthentication.getAuthorizationUri(),
|
||||
authorizationCodeRequestAuthentication.getClientId(),
|
||||
(Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri,
|
||||
authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(),
|
||||
authorizationCodeRequestAuthentication.getAdditionalParameters());
|
||||
authorizationCodeRequestAuthenticationResult.setAuthenticated(true);
|
||||
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult);
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error,
|
||||
authorizationCodeRequestAuthenticationResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AuthorizationConsent.Builder} and additional information
|
||||
* and is used when customizing the building of the {@link OAuth2AuthorizationConsent}.
|
||||
* An {@link OAuth2AuthenticationContext} that holds an
|
||||
* {@link OAuth2AuthorizationConsent.Builder} and additional information and is used when
|
||||
* customizing the building of the {@link OAuth2AuthorizationConsent}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Joe Grandja
|
||||
@@ -40,6 +41,7 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationConsentAuthenticationProvider#setAuthorizationConsentCustomizer(Consumer)
|
||||
*/
|
||||
public final class OAuth2AuthorizationConsentAuthenticationContext implements OAuth2AuthenticationContext {
|
||||
|
||||
private final Map<Object, Object> context;
|
||||
|
||||
private OAuth2AuthorizationConsentAuthenticationContext(Map<Object, Object> context) {
|
||||
@@ -60,8 +62,8 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AuthorizationConsent.Builder authorization consent builder}.
|
||||
*
|
||||
* Returns the {@link OAuth2AuthorizationConsent.Builder authorization consent
|
||||
* builder}.
|
||||
* @return the {@link OAuth2AuthorizationConsent.Builder}
|
||||
*/
|
||||
public OAuth2AuthorizationConsent.Builder getAuthorizationConsent() {
|
||||
@@ -70,7 +72,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Returns the {@link RegisteredClient registered client}.
|
||||
*
|
||||
* @return the {@link RegisteredClient}
|
||||
*/
|
||||
public RegisteredClient getRegisteredClient() {
|
||||
@@ -79,7 +80,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @return the {@link OAuth2Authorization}
|
||||
*/
|
||||
public OAuth2Authorization getAuthorization() {
|
||||
@@ -88,7 +88,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AuthorizationRequest authorization request}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthorizationRequest}
|
||||
*/
|
||||
public OAuth2AuthorizationRequest getAuthorizationRequest() {
|
||||
@@ -96,8 +95,8 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided {@link OAuth2AuthorizationConsentAuthenticationToken}.
|
||||
*
|
||||
* Constructs a new {@link Builder} with the provided
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationToken}.
|
||||
* @param authentication the {@link OAuth2AuthorizationConsentAuthenticationToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -108,15 +107,16 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
/**
|
||||
* A builder for {@link OAuth2AuthorizationConsentAuthenticationContext}.
|
||||
*/
|
||||
public static final class Builder extends AbstractBuilder<OAuth2AuthorizationConsentAuthenticationContext, Builder> {
|
||||
public static final class Builder
|
||||
extends AbstractBuilder<OAuth2AuthorizationConsentAuthenticationContext, Builder> {
|
||||
|
||||
private Builder(OAuth2AuthorizationConsentAuthenticationToken authentication) {
|
||||
super(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent builder}.
|
||||
*
|
||||
* Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent
|
||||
* builder}.
|
||||
* @param authorizationConsent the {@link OAuth2AuthorizationConsent.Builder}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -126,7 +126,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Sets the {@link RegisteredClient registered client}.
|
||||
*
|
||||
* @param registeredClient the {@link RegisteredClient}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -136,7 +135,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @param authorization the {@link OAuth2Authorization}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -146,7 +144,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2AuthorizationRequest authorization request}.
|
||||
*
|
||||
* @param authorizationRequest the {@link OAuth2AuthorizationRequest}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -156,7 +153,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA
|
||||
|
||||
/**
|
||||
* Builds a new {@link OAuth2AuthorizationConsentAuthenticationContext}.
|
||||
*
|
||||
* @return the {@link OAuth2AuthorizationConsentAuthenticationContext}
|
||||
*/
|
||||
public OAuth2AuthorizationConsentAuthenticationContext build() {
|
||||
|
||||
@@ -50,8 +50,8 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Consent
|
||||
* used in the Authorization Code Grant.
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization
|
||||
* Consent used in the Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.4.0
|
||||
@@ -63,24 +63,33 @@ import org.springframework.util.StringUtils;
|
||||
* @see OAuth2AuthorizationConsentService
|
||||
*/
|
||||
public final class OAuth2AuthorizationConsentAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1";
|
||||
|
||||
private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
|
||||
private OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator();
|
||||
|
||||
private Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationConsentAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationConsentAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param authorizationConsentService the authorization consent service
|
||||
*/
|
||||
public OAuth2AuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null");
|
||||
@@ -92,19 +101,20 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
if (authentication instanceof OAuth2DeviceAuthorizationConsentAuthenticationToken) {
|
||||
// This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code Grant,
|
||||
// return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider handle it instead
|
||||
// This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code
|
||||
// Grant,
|
||||
// return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider
|
||||
// handle it instead
|
||||
return null;
|
||||
}
|
||||
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication =
|
||||
(OAuth2AuthorizationConsentAuthenticationToken) authentication;
|
||||
OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = (OAuth2AuthorizationConsentAuthenticationToken) authentication;
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
authorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(authorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE,
|
||||
authorizationConsentAuthentication, null, null);
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication,
|
||||
null, null);
|
||||
}
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -114,12 +124,12 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
// The 'in-flight' authorization must be associated to the current principal
|
||||
Authentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal();
|
||||
if (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE,
|
||||
authorizationConsentAuthentication, null, null);
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication,
|
||||
null, null);
|
||||
}
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
|
||||
authorizationConsentAuthentication.getClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findByClientId(authorizationConsentAuthentication.getClientId());
|
||||
if (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID,
|
||||
authorizationConsentAuthentication, registeredClient, null);
|
||||
@@ -129,22 +139,23 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
OAuth2AuthorizationRequest authorizationRequest = authorization
|
||||
.getAttribute(OAuth2AuthorizationRequest.class.getName());
|
||||
Set<String> requestedScopes = authorizationRequest.getScopes();
|
||||
Set<String> authorizedScopes = new HashSet<>(authorizationConsentAuthentication.getScopes());
|
||||
if (!requestedScopes.containsAll(authorizedScopes)) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE,
|
||||
authorizationConsentAuthentication, registeredClient, authorizationRequest);
|
||||
throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationConsentAuthentication,
|
||||
registeredClient, authorizationRequest);
|
||||
}
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Validated authorization consent request parameters");
|
||||
}
|
||||
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
|
||||
authorization.getRegisteredClientId(), authorization.getPrincipalName());
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null ?
|
||||
currentAuthorizationConsent.getScopes() : Collections.emptySet();
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
|
||||
.findById(authorization.getRegisteredClientId(), authorization.getPrincipalName());
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null
|
||||
? currentAuthorizationConsent.getScopes() : Collections.emptySet();
|
||||
|
||||
if (!currentAuthorizedScopes.isEmpty()) {
|
||||
for (String requestedScope : requestedScopes) {
|
||||
@@ -165,9 +176,10 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
this.logger.trace("Retrieved existing authorization consent");
|
||||
}
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent);
|
||||
} else {
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(
|
||||
authorization.getRegisteredClientId(), authorization.getPrincipalName());
|
||||
}
|
||||
else {
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(),
|
||||
authorization.getPrincipalName());
|
||||
}
|
||||
authorizedScopes.forEach(authorizationConsentBuilder::scope);
|
||||
|
||||
@@ -214,8 +226,8 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
}
|
||||
}
|
||||
|
||||
OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(
|
||||
authorizationConsentAuthentication, registeredClient, authorization, authorizedScopes);
|
||||
OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationConsentAuthentication,
|
||||
registeredClient, authorization, authorizedScopes);
|
||||
OAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext);
|
||||
if (authorizationCode == null) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
@@ -228,12 +240,12 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
}
|
||||
|
||||
OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization)
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.token(authorizationCode)
|
||||
.attributes(attrs -> {
|
||||
attrs.remove(OAuth2ParameterNames.STATE);
|
||||
})
|
||||
.build();
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.token(authorizationCode)
|
||||
.attributes(attrs -> {
|
||||
attrs.remove(OAuth2ParameterNames.STATE);
|
||||
})
|
||||
.build();
|
||||
this.authorizationService.save(updatedAuthorization);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -249,9 +261,9 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
this.logger.trace("Authenticated authorization consent request");
|
||||
}
|
||||
|
||||
return new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationRequest.getAuthorizationUri(), registeredClient.getClientId(), principal, authorizationCode,
|
||||
redirectUri, authorizationRequest.getState(), authorizedScopes);
|
||||
return new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationRequest.getAuthorizationUri(),
|
||||
registeredClient.getClientId(), principal, authorizationCode, redirectUri,
|
||||
authorizationRequest.getState(), authorizedScopes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -260,36 +272,42 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}.
|
||||
*
|
||||
* @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the
|
||||
* {@link OAuth2AuthorizationCode}.
|
||||
* @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates
|
||||
* the {@link OAuth2AuthorizationCode}
|
||||
*/
|
||||
public void setAuthorizationCodeGenerator(OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {
|
||||
public void setAuthorizationCodeGenerator(
|
||||
OAuth2TokenGenerator<OAuth2AuthorizationCode> authorizationCodeGenerator) {
|
||||
Assert.notNull(authorizationCodeGenerator, "authorizationCodeGenerator cannot be null");
|
||||
this.authorizationCodeGenerator = authorizationCodeGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationConsentAuthenticationContext}
|
||||
* containing an {@link OAuth2AuthorizationConsent.Builder} and additional context information.
|
||||
* Sets the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an
|
||||
* {@link OAuth2AuthorizationConsent.Builder} and additional context information.
|
||||
*
|
||||
* <p>
|
||||
* The following context attributes are available:
|
||||
* <ul>
|
||||
* <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization consent
|
||||
* prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>
|
||||
* <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization
|
||||
* consent prior to
|
||||
* {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>
|
||||
* <li>The {@link Authentication} of type
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationToken}.</li>
|
||||
* <li>The {@link RegisteredClient} associated with the authorization request.</li>
|
||||
* <li>The {@link OAuth2Authorization} associated with the state token presented in the
|
||||
* authorization consent request.</li>
|
||||
* <li>The {@link OAuth2AuthorizationRequest} associated with the authorization consent request.</li>
|
||||
* <li>The {@link OAuth2Authorization} associated with the state token presented in
|
||||
* the authorization consent request.</li>
|
||||
* <li>The {@link OAuth2AuthorizationRequest} associated with the authorization
|
||||
* consent request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param authorizationConsentCustomizer the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an {@link OAuth2AuthorizationConsent.Builder}
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an
|
||||
* {@link OAuth2AuthorizationConsent.Builder}
|
||||
*/
|
||||
public void setAuthorizationConsentCustomizer(Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {
|
||||
public void setAuthorizationConsentCustomizer(
|
||||
Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {
|
||||
Assert.notNull(authorizationConsentCustomizer, "authorizationConsentCustomizer cannot be null");
|
||||
this.authorizationConsentCustomizer = authorizationConsentCustomizer;
|
||||
}
|
||||
@@ -313,9 +331,8 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
}
|
||||
|
||||
private static boolean isPrincipalAuthenticated(Authentication principal) {
|
||||
return principal != null &&
|
||||
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
|
||||
principal.isAuthenticated();
|
||||
return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())
|
||||
&& principal.isAuthenticated();
|
||||
}
|
||||
|
||||
private static void throwError(String errorCode, String parameterName,
|
||||
@@ -330,29 +347,29 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A
|
||||
RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) {
|
||||
|
||||
String redirectUri = resolveRedirectUri(authorizationRequest, registeredClient);
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) &&
|
||||
(parameterName.equals(OAuth2ParameterNames.CLIENT_ID) ||
|
||||
parameterName.equals(OAuth2ParameterNames.STATE))) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST)
|
||||
&& (parameterName.equals(OAuth2ParameterNames.CLIENT_ID)
|
||||
|| parameterName.equals(OAuth2ParameterNames.STATE))) {
|
||||
redirectUri = null; // Prevent redirects
|
||||
}
|
||||
|
||||
String state = authorizationRequest != null ?
|
||||
authorizationRequest.getState() :
|
||||
authorizationConsentAuthentication.getState();
|
||||
Set<String> requestedScopes = authorizationRequest != null ?
|
||||
authorizationRequest.getScopes() :
|
||||
authorizationConsentAuthentication.getScopes();
|
||||
String state = authorizationRequest != null ? authorizationRequest.getState()
|
||||
: authorizationConsentAuthentication.getState();
|
||||
Set<String> requestedScopes = authorizationRequest != null ? authorizationRequest.getScopes()
|
||||
: authorizationConsentAuthentication.getScopes();
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationConsentAuthentication.getAuthorizationUri(), authorizationConsentAuthentication.getClientId(),
|
||||
(Authentication) authorizationConsentAuthentication.getPrincipal(), redirectUri,
|
||||
state, requestedScopes, null);
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
|
||||
authorizationConsentAuthentication.getAuthorizationUri(),
|
||||
authorizationConsentAuthentication.getClientId(),
|
||||
(Authentication) authorizationConsentAuthentication.getPrincipal(), redirectUri, state, requestedScopes,
|
||||
null);
|
||||
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult);
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error,
|
||||
authorizationCodeRequestAuthenticationResult);
|
||||
}
|
||||
|
||||
private static String resolveRedirectUri(OAuth2AuthorizationRequest authorizationRequest, RegisteredClient registeredClient) {
|
||||
private static String resolveRedirectUri(OAuth2AuthorizationRequest authorizationRequest,
|
||||
RegisteredClient registeredClient) {
|
||||
if (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) {
|
||||
return authorizationRequest.getRedirectUri();
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation for the OAuth 2.0 Authorization Consent
|
||||
* used in the Authorization Code Grant.
|
||||
* An {@link Authentication} implementation for the OAuth 2.0 Authorization Consent used
|
||||
* in the Authorization Code Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.4.0
|
||||
@@ -37,17 +37,24 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationCodeRequestAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String authorizationUri;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final String state;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationConsentAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2AuthorizationConsentAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
@@ -55,8 +62,9 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
* @param scopes the requested (or authorized) scope(s)
|
||||
* @param additionalParameters the additional parameters
|
||||
*/
|
||||
public OAuth2AuthorizationConsentAuthenticationToken(String authorizationUri, String clientId, Authentication principal,
|
||||
String state, @Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
|
||||
public OAuth2AuthorizationConsentAuthenticationToken(String authorizationUri, String clientId,
|
||||
Authentication principal, String state, @Nullable Set<String> scopes,
|
||||
@Nullable Map<String, Object> additionalParameters) {
|
||||
super(Collections.emptyList());
|
||||
Assert.hasText(authorizationUri, "authorizationUri cannot be empty");
|
||||
Assert.hasText(clientId, "clientId cannot be empty");
|
||||
@@ -66,14 +74,9 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
this.clientId = clientId;
|
||||
this.principal = principal;
|
||||
this.state = state;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ?
|
||||
new HashSet<>(scopes) :
|
||||
Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ?
|
||||
new HashMap<>(additionalParameters) :
|
||||
Collections.emptyMap());
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@@ -89,7 +92,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
|
||||
/**
|
||||
* Returns the authorization URI.
|
||||
*
|
||||
* @return the authorization URI
|
||||
*/
|
||||
public String getAuthorizationUri() {
|
||||
@@ -98,7 +100,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
|
||||
/**
|
||||
* Returns the client identifier.
|
||||
*
|
||||
* @return the client identifier
|
||||
*/
|
||||
public String getClientId() {
|
||||
@@ -107,7 +108,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
|
||||
/**
|
||||
* Returns the state.
|
||||
*
|
||||
* @return the state
|
||||
*/
|
||||
public String getState() {
|
||||
@@ -116,8 +116,8 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
|
||||
/**
|
||||
* Returns the requested (or authorized) scope(s).
|
||||
*
|
||||
* @return the requested (or authorized) scope(s), or an empty {@code Set} if not available
|
||||
* @return the requested (or authorized) scope(s), or an empty {@code Set} if not
|
||||
* available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
@@ -125,7 +125,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters, or an empty {@code Map} if not available
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
|
||||
@@ -27,24 +27,29 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base implementation of an {@link Authentication} representing an OAuth 2.0 Authorization Grant.
|
||||
* Base implementation of an {@link Authentication} representing an OAuth 2.0
|
||||
* Authorization Grant.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.0
|
||||
* @see AbstractAuthenticationToken
|
||||
* @see AuthorizationGrantType
|
||||
* @see OAuth2ClientAuthenticationToken
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3">Section 1.3 Authorization Grant</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-1.3">Section
|
||||
* 1.3 Authorization Grant</a>
|
||||
*/
|
||||
public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final AuthorizationGrantType authorizationGrantType;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Sub-class constructor.
|
||||
*
|
||||
* @param authorizationGrantType the authorization grant type
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param additionalParameters the additional parameters
|
||||
@@ -57,14 +62,11 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent
|
||||
this.authorizationGrantType = authorizationGrantType;
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ?
|
||||
new HashMap<>(additionalParameters) :
|
||||
Collections.emptyMap());
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authorization grant type.
|
||||
*
|
||||
* @return the authorization grant type
|
||||
*/
|
||||
public AuthorizationGrantType getGrantType() {
|
||||
@@ -83,10 +85,10 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
return this.additionalParameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,16 +42,22 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@Transient
|
||||
public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
private final RegisteredClient registeredClient;
|
||||
|
||||
private final ClientAuthenticationMethod clientAuthenticationMethod;
|
||||
|
||||
private final Object credentials;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2ClientAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param clientId the client identifier
|
||||
* @param clientAuthenticationMethod the authentication method used by the client
|
||||
* @param credentials the client credentials
|
||||
@@ -66,19 +72,19 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
|
||||
this.registeredClient = null;
|
||||
this.clientAuthenticationMethod = clientAuthenticationMethod;
|
||||
this.credentials = credentials;
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ? additionalParameters : Collections.emptyMap());
|
||||
this.additionalParameters = Collections
|
||||
.unmodifiableMap(additionalParameters != null ? additionalParameters : Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2ClientAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param registeredClient the authenticated registered client
|
||||
* @param clientAuthenticationMethod the authentication method used by the client
|
||||
* @param credentials the client credentials
|
||||
*/
|
||||
public OAuth2ClientAuthenticationToken(RegisteredClient registeredClient, ClientAuthenticationMethod clientAuthenticationMethod,
|
||||
@Nullable Object credentials) {
|
||||
public OAuth2ClientAuthenticationToken(RegisteredClient registeredClient,
|
||||
ClientAuthenticationMethod clientAuthenticationMethod, @Nullable Object credentials) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||
Assert.notNull(clientAuthenticationMethod, "clientAuthenticationMethod cannot be null");
|
||||
@@ -102,9 +108,10 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authenticated {@link RegisteredClient registered client}, or {@code null} if not authenticated.
|
||||
*
|
||||
* @return the authenticated {@link RegisteredClient}, or {@code null} if not authenticated
|
||||
* Returns the authenticated {@link RegisteredClient registered client}, or
|
||||
* {@code null} if not authenticated.
|
||||
* @return the authenticated {@link RegisteredClient}, or {@code null} if not
|
||||
* authenticated
|
||||
*/
|
||||
@Nullable
|
||||
public RegisteredClient getRegisteredClient() {
|
||||
@@ -112,8 +119,8 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ClientAuthenticationMethod authentication method} used by the client.
|
||||
*
|
||||
* Returns the {@link ClientAuthenticationMethod authentication method} used by the
|
||||
* client.
|
||||
* @return the {@link ClientAuthenticationMethod} used by the client
|
||||
*/
|
||||
public ClientAuthenticationMethod getClientAuthenticationMethod() {
|
||||
@@ -122,7 +129,6 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
|
||||
@@ -46,7 +46,8 @@ import org.springframework.util.CollectionUtils;
|
||||
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials Grant.
|
||||
* An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials
|
||||
* Grant.
|
||||
*
|
||||
* @author Alexey Nesterov
|
||||
* @author Joe Grandja
|
||||
@@ -55,18 +56,26 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2AccessTokenAuthenticationToken
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2TokenGenerator
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4">Section 4.4 Client Credentials Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2">Section 4.4.2 Access Token Request</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-4.4">Section 4.4 Client
|
||||
* Credentials Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-4.4.2">Section 4.4.2 Access
|
||||
* Token Request</a>
|
||||
*/
|
||||
public final class OAuth2ClientCredentialsAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
@@ -81,11 +90,10 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
|
||||
(OAuth2ClientCredentialsAuthenticationToken) authentication;
|
||||
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = (OAuth2ClientCredentialsAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(clientCredentialsAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
clientCredentialsAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -144,9 +152,11 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
|
||||
.authorizedScopes(authorizedScopes);
|
||||
// @formatter:on
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.accessToken(accessToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials Grant.
|
||||
* An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials
|
||||
* Grant.
|
||||
*
|
||||
* @author Alexey Nesterov
|
||||
* @since 0.0.1
|
||||
@@ -33,28 +34,28 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
* @see OAuth2ClientCredentialsAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param scopes the requested scope(s)
|
||||
* @param additionalParameters the additional parameters
|
||||
*/
|
||||
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal,
|
||||
@Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
|
||||
public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, @Nullable Set<String> scopes,
|
||||
@Nullable Map<String, Object> additionalParameters) {
|
||||
super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters);
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the requested scope(s).
|
||||
*
|
||||
* @return the requested scope(s), or an empty {@code Set} if not available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -64,20 +64,23 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
|
||||
private Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationProvider} using
|
||||
* the provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param authorizationConsentService the authorization consent service
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationConsentAuthenticationProvider(
|
||||
RegisteredClientRepository registeredClientRepository,
|
||||
public OAuth2DeviceAuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
@@ -90,11 +93,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication =
|
||||
(OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication;
|
||||
OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication;
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
deviceAuthorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(deviceAuthorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);
|
||||
}
|
||||
@@ -109,8 +111,8 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);
|
||||
}
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
|
||||
deviceAuthorizationConsentAuthentication.getClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findByClientId(deviceAuthorizationConsentAuthentication.getClientId());
|
||||
if (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);
|
||||
}
|
||||
@@ -129,10 +131,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
this.logger.trace("Validated device authorization consent request parameters");
|
||||
}
|
||||
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
|
||||
authorization.getRegisteredClientId(), principal.getName());
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null ?
|
||||
currentAuthorizationConsent.getScopes() : Collections.emptySet();
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
|
||||
.findById(authorization.getRegisteredClientId(), principal.getName());
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null
|
||||
? currentAuthorizationConsent.getScopes() : Collections.emptySet();
|
||||
|
||||
if (!currentAuthorizedScopes.isEmpty()) {
|
||||
for (String requestedScope : requestedScopes) {
|
||||
@@ -148,9 +150,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
this.logger.trace("Retrieved existing authorization consent");
|
||||
}
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent);
|
||||
} else {
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(
|
||||
authorization.getRegisteredClientId(), principal.getName());
|
||||
}
|
||||
else {
|
||||
authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(),
|
||||
principal.getName());
|
||||
}
|
||||
authorizedScopes.forEach(authorizationConsentBuilder::scope);
|
||||
|
||||
@@ -184,12 +187,12 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
}
|
||||
}
|
||||
authorization = OAuth2Authorization.from(authorization)
|
||||
.token(deviceCodeToken.getToken(), metadata ->
|
||||
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.token(userCodeToken.getToken(), metadata ->
|
||||
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
|
||||
.build();
|
||||
.token(deviceCodeToken.getToken(),
|
||||
metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.token(userCodeToken.getToken(),
|
||||
metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
|
||||
.build();
|
||||
this.authorizationService.save(authorization);
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Invalidated device code and user code because authorization consent was denied");
|
||||
@@ -206,12 +209,12 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
}
|
||||
|
||||
authorization = OAuth2Authorization.from(authorization)
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.token(userCodeToken.getToken(), metadata ->
|
||||
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.SCOPE))
|
||||
.build();
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.token(userCodeToken.getToken(),
|
||||
metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE))
|
||||
.attributes(attrs -> attrs.remove(OAuth2ParameterNames.SCOPE))
|
||||
.build();
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -230,33 +233,36 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationConsentAuthenticationContext}
|
||||
* containing an {@link OAuth2AuthorizationConsent.Builder} and additional context information.
|
||||
* Sets the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an
|
||||
* {@link OAuth2AuthorizationConsent.Builder} and additional context information.
|
||||
*
|
||||
* <p>
|
||||
* The following context attributes are available:
|
||||
* <ul>
|
||||
* <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization consent
|
||||
* prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>
|
||||
* <li>The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization
|
||||
* consent prior to
|
||||
* {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.</li>
|
||||
* <li>The {@link Authentication} of type
|
||||
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.</li>
|
||||
* <li>The {@link RegisteredClient} associated with the device authorization request.</li>
|
||||
* <li>The {@link OAuth2Authorization} associated with the state token presented in the
|
||||
* device authorization consent request.</li>
|
||||
* <li>The {@link RegisteredClient} associated with the device authorization
|
||||
* request.</li>
|
||||
* <li>The {@link OAuth2Authorization} associated with the state token presented in
|
||||
* the device authorization consent request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param authorizationConsentCustomizer the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an {@link OAuth2AuthorizationConsent.Builder}
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationContext} containing an
|
||||
* {@link OAuth2AuthorizationConsent.Builder}
|
||||
*/
|
||||
public void setAuthorizationConsentCustomizer(Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {
|
||||
public void setAuthorizationConsentCustomizer(
|
||||
Consumer<OAuth2AuthorizationConsentAuthenticationContext> authorizationConsentCustomizer) {
|
||||
Assert.notNull(authorizationConsentCustomizer, "authorizationConsentCustomizer cannot be null");
|
||||
this.authorizationConsentCustomizer = authorizationConsentCustomizer;
|
||||
}
|
||||
|
||||
private static boolean isPrincipalAuthenticated(Authentication principal) {
|
||||
return principal != null &&
|
||||
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
|
||||
principal.isAuthenticated();
|
||||
return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())
|
||||
&& principal.isAuthenticated();
|
||||
}
|
||||
|
||||
private static void throwError(String errorCode, String parameterName) {
|
||||
|
||||
@@ -27,8 +27,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation for the Device Authorization Consent used
|
||||
* in the OAuth 2.0 Device Authorization Grant.
|
||||
* An {@link Authentication} implementation for the Device Authorization Consent used in
|
||||
* the OAuth 2.0 Device Authorization Grant.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 1.1
|
||||
@@ -36,13 +36,16 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2DeviceAuthorizationConsentAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2AuthorizationConsentAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String userCode;
|
||||
|
||||
private final Set<String> requestedScopes;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
@@ -62,8 +65,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param authorizationUri the authorization URI
|
||||
* @param clientId the client identifier
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
@@ -78,16 +81,13 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A
|
||||
super(authorizationUri, clientId, principal, state, authorizedScopes, null);
|
||||
Assert.hasText(userCode, "userCode cannot be empty");
|
||||
this.userCode = userCode;
|
||||
this.requestedScopes = Collections.unmodifiableSet(
|
||||
requestedScopes != null ?
|
||||
new HashSet<>(requestedScopes) :
|
||||
Collections.emptySet());
|
||||
this.requestedScopes = Collections
|
||||
.unmodifiableSet(requestedScopes != null ? new HashSet<>(requestedScopes) : Collections.emptySet());
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user code.
|
||||
*
|
||||
* @return the user code
|
||||
*/
|
||||
public String getUserCode() {
|
||||
@@ -96,7 +96,6 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A
|
||||
|
||||
/**
|
||||
* Returns the requested scopes.
|
||||
*
|
||||
* @return the requested scopes
|
||||
*/
|
||||
public Set<String> getRequestedScopes() {
|
||||
|
||||
@@ -63,8 +63,11 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2DeviceCodeAuthenticationProvider
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2TokenGenerator
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0 Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628#section-3.1">Section 3.1 Device Authorization Request</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0
|
||||
* Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc8628#section-3.1">Section 3.1 Device
|
||||
* Authorization Request</a>
|
||||
*/
|
||||
public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
@@ -73,13 +76,16 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private OAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator = new OAuth2DeviceCodeGenerator();
|
||||
|
||||
private OAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator = new OAuth2UserCodeGenerator();
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationProvider} using
|
||||
* the provided parameters.
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationRequestAuthenticationProvider(OAuth2AuthorizationService authorizationService) {
|
||||
@@ -89,11 +95,10 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication =
|
||||
(OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication;
|
||||
OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(deviceAuthorizationRequestAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
deviceAuthorizationRequestAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -171,8 +176,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
this.logger.trace("Authenticated device authorization request");
|
||||
}
|
||||
|
||||
return new OAuth2DeviceAuthorizationRequestAuthenticationToken(
|
||||
clientPrincipal, requestedScopes, deviceCode, userCode);
|
||||
return new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, requestedScopes, deviceCode,
|
||||
userCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -182,8 +187,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2DeviceCode}.
|
||||
*
|
||||
* @param deviceCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2DeviceCode}
|
||||
* @param deviceCodeGenerator the {@link OAuth2TokenGenerator} that generates the
|
||||
* {@link OAuth2DeviceCode}
|
||||
*/
|
||||
public void setDeviceCodeGenerator(OAuth2TokenGenerator<OAuth2DeviceCode> deviceCodeGenerator) {
|
||||
Assert.notNull(deviceCodeGenerator, "deviceCodeGenerator cannot be null");
|
||||
@@ -192,8 +197,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2UserCode}.
|
||||
*
|
||||
* @param userCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2UserCode}
|
||||
* @param userCodeGenerator the {@link OAuth2TokenGenerator} that generates the
|
||||
* {@link OAuth2UserCode}
|
||||
*/
|
||||
public void setUserCodeGenerator(OAuth2TokenGenerator<OAuth2UserCode> userCodeGenerator) {
|
||||
Assert.notNull(userCodeGenerator, "userCodeGenerator cannot be null");
|
||||
@@ -207,18 +212,19 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
|
||||
private static final class OAuth2DeviceCodeGenerator implements OAuth2TokenGenerator<OAuth2DeviceCode> {
|
||||
|
||||
private final StringKeyGenerator deviceCodeGenerator =
|
||||
new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
private final StringKeyGenerator deviceCodeGenerator = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder().withoutPadding(), 96);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public OAuth2DeviceCode generate(OAuth2TokenContext context) {
|
||||
if (context.getTokenType() == null ||
|
||||
!OAuth2ParameterNames.DEVICE_CODE.equals(context.getTokenType().getValue())) {
|
||||
if (context.getTokenType() == null
|
||||
|| !OAuth2ParameterNames.DEVICE_CODE.equals(context.getTokenType().getValue())) {
|
||||
return null;
|
||||
}
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());
|
||||
Instant expiresAt = issuedAt
|
||||
.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());
|
||||
return new OAuth2DeviceCode(this.deviceCodeGenerator.generateKey(), issuedAt, expiresAt);
|
||||
}
|
||||
|
||||
@@ -256,12 +262,13 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem
|
||||
@Nullable
|
||||
@Override
|
||||
public OAuth2UserCode generate(OAuth2TokenContext context) {
|
||||
if (context.getTokenType() == null ||
|
||||
!OAuth2ParameterNames.USER_CODE.equals(context.getTokenType().getValue())) {
|
||||
if (context.getTokenType() == null
|
||||
|| !OAuth2ParameterNames.USER_CODE.equals(context.getTokenType().getValue())) {
|
||||
return null;
|
||||
}
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());
|
||||
Instant expiresAt = issuedAt
|
||||
.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive());
|
||||
return new OAuth2UserCode(this.userCodeGenerator.generateKey(), issuedAt, expiresAt);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation for the Device Authorization Request
|
||||
* used in the OAuth 2.0 Device Authorization Grant.
|
||||
* An {@link Authentication} implementation for the Device Authorization Request used in
|
||||
* the OAuth 2.0 Device Authorization Grant.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 1.1
|
||||
@@ -40,17 +40,24 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2DeviceAuthorizationRequestAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final String authorizationUri;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
private final OAuth2DeviceCode deviceCode;
|
||||
|
||||
private final OAuth2UserCode userCode;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param authorizationUri the authorization {@code URI}
|
||||
* @param scopes the requested scope(s)
|
||||
@@ -63,37 +70,29 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
Assert.hasText(authorizationUri, "authorizationUri cannot be empty");
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.authorizationUri = authorizationUri;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ?
|
||||
new HashSet<>(scopes) :
|
||||
Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ?
|
||||
new HashMap<>(additionalParameters) :
|
||||
Collections.emptyMap());
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
this.deviceCode = null;
|
||||
this.userCode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param scopes the requested scope(s)
|
||||
* @param deviceCode the {@link OAuth2DeviceCode}
|
||||
* @param userCode the {@link OAuth2UserCode}
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal, @Nullable Set<String> scopes,
|
||||
OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) {
|
||||
public OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal,
|
||||
@Nullable Set<String> scopes, OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
Assert.notNull(deviceCode, "deviceCode cannot be null");
|
||||
Assert.notNull(userCode, "userCode cannot be null");
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ?
|
||||
new HashSet<>(scopes) :
|
||||
Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.deviceCode = deviceCode;
|
||||
this.userCode = userCode;
|
||||
this.authorizationUri = null;
|
||||
@@ -113,7 +112,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
|
||||
/**
|
||||
* Returns the authorization {@code URI}.
|
||||
*
|
||||
* @return the authorization {@code URI}
|
||||
*/
|
||||
public String getAuthorizationUri() {
|
||||
@@ -122,7 +120,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
|
||||
/**
|
||||
* Returns the requested scope(s).
|
||||
*
|
||||
* @return the requested scope(s)
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
@@ -131,7 +128,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
|
||||
/**
|
||||
* Returns the device code.
|
||||
*
|
||||
* @return the device code
|
||||
*/
|
||||
public OAuth2DeviceCode getDeviceCode() {
|
||||
@@ -140,7 +136,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
|
||||
/**
|
||||
* Returns the user code.
|
||||
*
|
||||
* @return the user code
|
||||
*/
|
||||
public OAuth2UserCode getUserCode() {
|
||||
@@ -149,7 +144,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
|
||||
@@ -60,30 +60,37 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2DeviceAuthorizationConsentAuthenticationProvider
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2TokenGenerator
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0 Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628#section-3.4">Section 3.4 Device Access Token Request</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628#section-3.5">Section 3.5 Device Access Token Response</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0
|
||||
* Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc8628#section-3.4">Section 3.4 Device Access
|
||||
* Token Request</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5">Section 3.5 Device Access
|
||||
* Token Response</a>
|
||||
*/
|
||||
public final class OAuth2DeviceCodeAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||
|
||||
private static final String DEVICE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5";
|
||||
static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE);
|
||||
static final String EXPIRED_TOKEN = "expired_token";
|
||||
static final String AUTHORIZATION_PENDING = "authorization_pending";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceCodeAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceCodeAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
*/
|
||||
public OAuth2DeviceCodeAuthenticationProvider(
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
public OAuth2DeviceCodeAuthenticationProvider(OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
@@ -93,19 +100,18 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication =
|
||||
(OAuth2DeviceCodeAuthenticationToken) authentication;
|
||||
OAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication = (OAuth2DeviceCodeAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(deviceCodeAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
deviceCodeAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
deviceCodeAuthentication.getDeviceCode(), DEVICE_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(deviceCodeAuthentication.getDeviceCode(), DEVICE_CODE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
@@ -119,12 +125,13 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
|
||||
|
||||
if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
|
||||
if (!deviceCode.isInvalidated()) {
|
||||
// Invalidate the device code given that a different client is attempting to use it
|
||||
// Invalidate the device code given that a different client is attempting
|
||||
// to use it
|
||||
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken());
|
||||
this.authorizationService.save(authorization);
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(LogMessage.format(
|
||||
"Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId()));
|
||||
this.logger.warn(LogMessage.format("Invalidated device code used by registered client '%s'",
|
||||
authorization.getRegisteredClientId()));
|
||||
}
|
||||
}
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
@@ -133,46 +140,46 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
|
||||
// In https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5,
|
||||
// the following error codes are defined:
|
||||
|
||||
// authorization_pending
|
||||
// The authorization request is still pending as the end user hasn't
|
||||
// yet completed the user-interaction steps (Section 3.3). The
|
||||
// client SHOULD repeat the access token request to the token
|
||||
// endpoint (a process known as polling). Before each new request,
|
||||
// the client MUST wait at least the number of seconds specified by
|
||||
// the "interval" parameter of the device authorization response (see
|
||||
// Section 3.2), or 5 seconds if none was provided, and respect any
|
||||
// increase in the polling interval required by the "slow_down"
|
||||
// error.
|
||||
// authorization_pending
|
||||
// The authorization request is still pending as the end user hasn't
|
||||
// yet completed the user-interaction steps (Section 3.3). The
|
||||
// client SHOULD repeat the access token request to the token
|
||||
// endpoint (a process known as polling). Before each new request,
|
||||
// the client MUST wait at least the number of seconds specified by
|
||||
// the "interval" parameter of the device authorization response (see
|
||||
// Section 3.2), or 5 seconds if none was provided, and respect any
|
||||
// increase in the polling interval required by the "slow_down"
|
||||
// error.
|
||||
if (!userCode.isInvalidated()) {
|
||||
OAuth2Error error = new OAuth2Error(AUTHORIZATION_PENDING, null, DEVICE_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
// slow_down
|
||||
// A variant of "authorization_pending", the authorization request is
|
||||
// still pending and polling should continue, but the interval MUST
|
||||
// be increased by 5 seconds for this and all subsequent requests.
|
||||
// NOTE: This error is not handled in the framework.
|
||||
// slow_down
|
||||
// A variant of "authorization_pending", the authorization request is
|
||||
// still pending and polling should continue, but the interval MUST
|
||||
// be increased by 5 seconds for this and all subsequent requests.
|
||||
// NOTE: This error is not handled in the framework.
|
||||
|
||||
// access_denied
|
||||
// The authorization request was denied.
|
||||
// access_denied
|
||||
// The authorization request was denied.
|
||||
if (deviceCode.isInvalidated()) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED, null, DEVICE_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
// expired_token
|
||||
// The "device_code" has expired, and the device authorization
|
||||
// session has concluded. The client MAY commence a new device
|
||||
// authorization request but SHOULD wait for user interaction before
|
||||
// restarting to avoid unnecessary polling.
|
||||
// expired_token
|
||||
// The "device_code" has expired, and the device authorization
|
||||
// session has concluded. The client MAY commence a new device
|
||||
// authorization request but SHOULD wait for user interaction before
|
||||
// restarting to avoid unnecessary polling.
|
||||
if (deviceCode.isExpired()) {
|
||||
// Invalidate the device code
|
||||
authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken());
|
||||
this.authorizationService.save(authorization);
|
||||
if (this.logger.isWarnEnabled()) {
|
||||
this.logger.warn(LogMessage.format(
|
||||
"Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId()));
|
||||
this.logger.warn(LogMessage.format("Invalidated device code used by registered client '%s'",
|
||||
authorization.getRegisteredClientId()));
|
||||
}
|
||||
OAuth2Error error = new OAuth2Error(EXPIRED_TOKEN, null, DEVICE_ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
@@ -217,9 +224,11 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat
|
||||
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()));
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.accessToken(accessToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation for the Device Access Token Request
|
||||
* used in the OAuth 2.0 Device Authorization Grant.
|
||||
* An {@link Authentication} implementation for the Device Access Token Request used in
|
||||
* the OAuth 2.0 Device Authorization Grant.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 1.1
|
||||
@@ -36,8 +36,8 @@ public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGran
|
||||
private final String deviceCode;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param deviceCode the device code
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param additionalParameters the additional parameters
|
||||
@@ -51,7 +51,6 @@ public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGran
|
||||
|
||||
/**
|
||||
* Returns the device code.
|
||||
*
|
||||
* @return the device code
|
||||
*/
|
||||
public String getDeviceCode() {
|
||||
|
||||
@@ -56,29 +56,35 @@ import org.springframework.util.Assert;
|
||||
* @see RegisteredClientRepository
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2AuthorizationConsentService
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0 Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628#section-3.3">Section 3.3 User Interaction</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc8628">OAuth 2.0
|
||||
* Device Authorization Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc8628#section-3.3">Section 3.3 User
|
||||
* Interaction</a>
|
||||
*/
|
||||
public final class OAuth2DeviceVerificationAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE);
|
||||
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR =
|
||||
new Base64StringKeyGenerator(Base64.getUrlEncoder());
|
||||
|
||||
private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator(
|
||||
Base64.getUrlEncoder());
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2AuthorizationConsentService authorizationConsentService;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param authorizationConsentService the authorization consent service
|
||||
*/
|
||||
public OAuth2DeviceVerificationAuthenticationProvider(
|
||||
RegisteredClientRepository registeredClientRepository,
|
||||
public OAuth2DeviceVerificationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
@@ -91,11 +97,10 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication =
|
||||
(OAuth2DeviceVerificationAuthenticationToken) authentication;
|
||||
OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = (OAuth2DeviceVerificationAuthenticationToken) authentication;
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
deviceVerificationAuthentication.getUserCode(), USER_CODE_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(deviceVerificationAuthentication.getUserCode(), USER_CODE_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
@@ -109,12 +114,13 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not authenticate device verification request since principal not authenticated");
|
||||
}
|
||||
// Return the device verification request as-is where isAuthenticated() is false
|
||||
// Return the device verification request as-is where isAuthenticated() is
|
||||
// false
|
||||
return deviceVerificationAuthentication;
|
||||
}
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findById(
|
||||
authorization.getRegisteredClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findById(authorization.getRegisteredClientId());
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Retrieved registered client");
|
||||
@@ -122,16 +128,16 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
|
||||
Set<String> requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);
|
||||
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById(
|
||||
registeredClient.getId(), principal.getName());
|
||||
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
|
||||
.findById(registeredClient.getId(), principal.getName());
|
||||
|
||||
if (requiresAuthorizationConsent(requestedScopes, currentAuthorizationConsent)) {
|
||||
String state = DEFAULT_STATE_GENERATOR.generateKey();
|
||||
authorization = OAuth2Authorization.from(authorization)
|
||||
.principalName(principal.getName())
|
||||
.attribute(Principal.class.getName(), principal)
|
||||
.attribute(OAuth2ParameterNames.STATE, state)
|
||||
.build();
|
||||
.principalName(principal.getName())
|
||||
.attribute(Principal.class.getName(), principal)
|
||||
.attribute(OAuth2ParameterNames.STATE, state)
|
||||
.build();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Generated device authorization consent state");
|
||||
@@ -143,11 +149,11 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
this.logger.trace("Saved authorization");
|
||||
}
|
||||
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null ?
|
||||
currentAuthorizationConsent.getScopes() : null;
|
||||
Set<String> currentAuthorizedScopes = currentAuthorizationConsent != null
|
||||
? currentAuthorizationConsent.getScopes() : null;
|
||||
|
||||
AuthorizationServerSettings authorizationServerSettings =
|
||||
AuthorizationServerContextHolder.getContext().getAuthorizationServerSettings();
|
||||
AuthorizationServerSettings authorizationServerSettings = AuthorizationServerContextHolder.getContext()
|
||||
.getAuthorizationServerSettings();
|
||||
String deviceVerificationUri = authorizationServerSettings.getDeviceVerificationEndpoint();
|
||||
|
||||
return new OAuth2DeviceAuthorizationConsentAuthenticationToken(deviceVerificationUri,
|
||||
@@ -183,11 +189,10 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
return OAuth2DeviceVerificationAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
private static boolean requiresAuthorizationConsent(
|
||||
Set<String> requestedScopes, OAuth2AuthorizationConsent authorizationConsent) {
|
||||
private static boolean requiresAuthorizationConsent(Set<String> requestedScopes,
|
||||
OAuth2AuthorizationConsent authorizationConsent) {
|
||||
|
||||
if (authorizationConsent != null &&
|
||||
authorizationConsent.getScopes().containsAll(requestedScopes)) {
|
||||
if (authorizationConsent != null && authorizationConsent.getScopes().containsAll(requestedScopes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -195,9 +200,8 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut
|
||||
}
|
||||
|
||||
private static boolean isPrincipalAuthenticated(Authentication principal) {
|
||||
return principal != null &&
|
||||
!AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) &&
|
||||
principal.isAuthenticated();
|
||||
return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass())
|
||||
&& principal.isAuthenticated();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,15 +35,20 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2DeviceVerificationAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final String userCode;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
* @param userCode the user code associated with the device authorization response
|
||||
* @param additionalParameters the additional parameters
|
||||
@@ -56,15 +61,13 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
|
||||
this.principal = principal;
|
||||
this.userCode = userCode;
|
||||
this.additionalParameters = Collections.unmodifiableMap(
|
||||
additionalParameters != null ?
|
||||
new HashMap<>(additionalParameters) :
|
||||
Collections.emptyMap());
|
||||
additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
|
||||
this.clientId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param principal the {@code Principal} (Resource Owner)
|
||||
* @param userCode the user code associated with the device authorization response
|
||||
* @param clientId the client identifier
|
||||
@@ -93,7 +96,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the user code.
|
||||
*
|
||||
* @return the user code
|
||||
*/
|
||||
public String getUserCode() {
|
||||
@@ -102,7 +104,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters, or an empty {@code Map} if not available
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
@@ -111,7 +112,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the client identifier.
|
||||
*
|
||||
* @return the client identifier
|
||||
*/
|
||||
public String getClientId() {
|
||||
|
||||
@@ -62,19 +62,28 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2AccessTokenAuthenticationToken
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OAuth2TokenGenerator
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-1.5">Section 1.5 Refresh Token Grant</a>
|
||||
* @see <a target="_blank" href="https://datatracker.ietf.org/doc/html/rfc6749#section-6">Section 6 Refreshing an Access Token</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-1.5">Section 1.5 Refresh Token
|
||||
* Grant</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://datatracker.ietf.org/doc/html/rfc6749#section-6">Section 6 Refreshing an
|
||||
* Access Token</a>
|
||||
*/
|
||||
public final class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
|
||||
|
||||
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
@@ -89,19 +98,18 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication =
|
||||
(OAuth2RefreshTokenAuthenticationToken) authentication;
|
||||
OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication = (OAuth2RefreshTokenAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(refreshTokenAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
refreshTokenAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
@@ -122,13 +130,16 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
|
||||
if (!refreshToken.isActive()) {
|
||||
// As per https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
// invalid_grant: The provided authorization grant (e.g., authorization code,
|
||||
// resource owner credentials) or refresh token is invalid, expired, revoked [...].
|
||||
// resource owner credentials) or refresh token is invalid, expired, revoked
|
||||
// [...].
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
|
||||
}
|
||||
|
||||
// As per https://tools.ietf.org/html/rfc6749#section-6
|
||||
// The requested scope MUST NOT include any scope not originally granted by the resource owner,
|
||||
// and if omitted is treated as equal to the scope originally granted by the resource owner.
|
||||
// The requested scope MUST NOT include any scope not originally granted by the
|
||||
// resource owner,
|
||||
// and if omitted is treated as equal to the scope originally granted by the
|
||||
// resource owner.
|
||||
Set<String> scopes = refreshTokenAuthentication.getScopes();
|
||||
Set<String> authorizedScopes = authorization.getAuthorizedScopes();
|
||||
if (!authorizedScopes.containsAll(scopes)) {
|
||||
@@ -174,10 +185,12 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
|
||||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) -> {
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims());
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims());
|
||||
metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false);
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.accessToken(accessToken);
|
||||
}
|
||||
|
||||
@@ -222,9 +235,10 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
|
||||
|
||||
idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(),
|
||||
generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims());
|
||||
authorizationBuilder.token(idToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(idToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()));
|
||||
}
|
||||
else {
|
||||
idToken = null;
|
||||
}
|
||||
|
||||
@@ -246,8 +260,8 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic
|
||||
this.logger.trace("Authenticated token request");
|
||||
}
|
||||
|
||||
return new OAuth2AccessTokenAuthenticationToken(
|
||||
registeredClient, clientPrincipal, accessToken, currentRefreshToken, additionalParameters);
|
||||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
currentRefreshToken, additionalParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -34,12 +34,14 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2RefreshTokenAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
|
||||
|
||||
private final String refreshToken;
|
||||
|
||||
private final Set<String> scopes;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param refreshToken the refresh token
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param scopes the requested scope(s)
|
||||
@@ -50,13 +52,11 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr
|
||||
super(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, additionalParameters);
|
||||
Assert.hasText(refreshToken, "refreshToken cannot be empty");
|
||||
this.refreshToken = refreshToken;
|
||||
this.scopes = Collections.unmodifiableSet(
|
||||
scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the refresh token.
|
||||
*
|
||||
* @return the refresh token
|
||||
*/
|
||||
public String getRefreshToken() {
|
||||
@@ -65,10 +65,10 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr
|
||||
|
||||
/**
|
||||
* Returns the requested scope(s).
|
||||
*
|
||||
* @return the requested scope(s), or an empty {@code Set} if not available
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
return this.scopes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,19 +50,25 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @see OAuth2TokenIntrospectionAuthenticationToken
|
||||
* @see RegisteredClientRepository
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.1">Section 2.1 Introspection Request</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7662#section-2.1">Section
|
||||
* 2.1 Introspection Request</a>
|
||||
*/
|
||||
public final class OAuth2TokenIntrospectionAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);
|
||||
private static final TypeDescriptor LIST_STRING_TYPE_DESCRIPTOR =
|
||||
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class));
|
||||
|
||||
private static final TypeDescriptor LIST_STRING_TYPE_DESCRIPTOR = TypeDescriptor.collection(List.class,
|
||||
TypeDescriptor.valueOf(String.class));
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
@@ -76,14 +82,13 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication =
|
||||
(OAuth2TokenIntrospectionAuthenticationToken) authentication;
|
||||
OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = (OAuth2TokenIntrospectionAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(tokenIntrospectionAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
tokenIntrospectionAuthentication);
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
tokenIntrospectionAuthentication.getToken(), null);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(tokenIntrospectionAuthentication.getToken(), null);
|
||||
if (authorization == null) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not authenticate token introspection request since token was not found");
|
||||
@@ -96,8 +101,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
this.logger.trace("Retrieved authorization with token");
|
||||
}
|
||||
|
||||
OAuth2Authorization.Token<OAuth2Token> authorizedToken =
|
||||
authorization.getToken(tokenIntrospectionAuthentication.getToken());
|
||||
OAuth2Authorization.Token<OAuth2Token> authorizedToken = authorization
|
||||
.getToken(tokenIntrospectionAuthentication.getToken());
|
||||
if (!authorizedToken.isActive()) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not introspect token since not active");
|
||||
@@ -106,7 +111,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
clientPrincipal, OAuth2TokenIntrospection.builder().build());
|
||||
}
|
||||
|
||||
RegisteredClient authorizedClient = this.registeredClientRepository.findById(authorization.getRegisteredClientId());
|
||||
RegisteredClient authorizedClient = this.registeredClientRepository
|
||||
.findById(authorization.getRegisteredClientId());
|
||||
OAuth2TokenIntrospection tokenClaims = withActiveTokenClaims(authorizedToken, authorizedClient);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -129,7 +135,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
if (!CollectionUtils.isEmpty(authorizedToken.getClaims())) {
|
||||
Map<String, Object> claims = convertClaimsIfNecessary(authorizedToken.getClaims());
|
||||
tokenClaims = OAuth2TokenIntrospection.withClaims(claims).active(true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
tokenClaims = OAuth2TokenIntrospection.builder(true);
|
||||
}
|
||||
|
||||
@@ -158,8 +165,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
|
||||
Object value = claims.get(OAuth2TokenIntrospectionClaimNames.ISS);
|
||||
if (value != null && !(value instanceof URL)) {
|
||||
URL convertedValue = ClaimConversionService.getSharedInstance()
|
||||
.convert(value, URL.class);
|
||||
URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class);
|
||||
if (convertedValue != null) {
|
||||
convertedClaims.put(OAuth2TokenIntrospectionClaimNames.ISS, convertedValue);
|
||||
}
|
||||
@@ -168,7 +174,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
value = claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE);
|
||||
if (value != null && !(value instanceof List)) {
|
||||
Object convertedValue = ClaimConversionService.getSharedInstance()
|
||||
.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);
|
||||
.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);
|
||||
if (convertedValue != null) {
|
||||
convertedClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, convertedValue);
|
||||
}
|
||||
@@ -177,7 +183,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut
|
||||
value = claims.get(OAuth2TokenIntrospectionClaimNames.AUD);
|
||||
if (value != null && !(value instanceof List)) {
|
||||
Object convertedValue = ClaimConversionService.getSharedInstance()
|
||||
.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);
|
||||
.convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR);
|
||||
if (convertedValue != null) {
|
||||
convertedClaims.put(OAuth2TokenIntrospectionClaimNames.AUD, convertedValue);
|
||||
}
|
||||
|
||||
@@ -37,16 +37,22 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2TokenIntrospectionAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String token;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final String tokenTypeHint;
|
||||
|
||||
private final Map<String, Object> additionalParameters;
|
||||
|
||||
private final OAuth2TokenIntrospection tokenClaims;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param token the token
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param tokenTypeHint the token type hint
|
||||
@@ -66,8 +72,8 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the
|
||||
* provided parameters.
|
||||
* @param token the token
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param tokenClaims the token claims
|
||||
@@ -83,7 +89,8 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
this.tokenTypeHint = null;
|
||||
this.additionalParameters = Collections.emptyMap();
|
||||
this.tokenClaims = tokenClaims;
|
||||
// Indicates that the request was authenticated, even though the token might not be active
|
||||
// Indicates that the request was authenticated, even though the token might not
|
||||
// be active
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@@ -99,7 +106,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the token.
|
||||
*
|
||||
* @return the token
|
||||
*/
|
||||
public String getToken() {
|
||||
@@ -108,7 +114,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the token type hint.
|
||||
*
|
||||
* @return the token type hint
|
||||
*/
|
||||
@Nullable
|
||||
@@ -118,7 +123,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the additional parameters.
|
||||
*
|
||||
* @return the additional parameters
|
||||
*/
|
||||
public Map<String, Object> getAdditionalParameters() {
|
||||
@@ -127,7 +131,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent
|
||||
|
||||
/**
|
||||
* Returns the token claims.
|
||||
*
|
||||
* @return the {@link OAuth2TokenIntrospection}
|
||||
*/
|
||||
public OAuth2TokenIntrospection getTokenClaims() {
|
||||
|
||||
@@ -39,15 +39,18 @@ import static org.springframework.security.oauth2.server.authorization.authentic
|
||||
* @since 0.0.3
|
||||
* @see OAuth2TokenRevocationAuthenticationToken
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section 2.1 Revocation Request</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7009#section-2.1">Section
|
||||
* 2.1 Revocation Request</a>
|
||||
*/
|
||||
public final class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
public OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService) {
|
||||
@@ -57,15 +60,14 @@ public final class OAuth2TokenRevocationAuthenticationProvider implements Authen
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication =
|
||||
(OAuth2TokenRevocationAuthenticationToken) authentication;
|
||||
OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = (OAuth2TokenRevocationAuthenticationToken) authentication;
|
||||
|
||||
OAuth2ClientAuthenticationToken clientPrincipal =
|
||||
getAuthenticatedClientElseThrowInvalidClient(tokenRevocationAuthentication);
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(
|
||||
tokenRevocationAuthentication);
|
||||
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
tokenRevocationAuthentication.getToken(), null);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(tokenRevocationAuthentication.getToken(), null);
|
||||
if (authorization == null) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Did not authenticate token revocation request since token was not found");
|
||||
@@ -95,4 +97,5 @@ public final class OAuth2TokenRevocationAuthenticationProvider implements Authen
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,20 +34,24 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2TokenRevocationAuthenticationProvider
|
||||
*/
|
||||
public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String token;
|
||||
|
||||
private final Authentication clientPrincipal;
|
||||
|
||||
private final String tokenTypeHint;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param token the token
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
* @param tokenTypeHint the token type hint
|
||||
*/
|
||||
public OAuth2TokenRevocationAuthenticationToken(String token,
|
||||
Authentication clientPrincipal, @Nullable String tokenTypeHint) {
|
||||
public OAuth2TokenRevocationAuthenticationToken(String token, Authentication clientPrincipal,
|
||||
@Nullable String tokenTypeHint) {
|
||||
super(Collections.emptyList());
|
||||
Assert.hasText(token, "token cannot be empty");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
@@ -57,20 +61,19 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param revokedToken the revoked token
|
||||
* @param clientPrincipal the authenticated client principal
|
||||
*/
|
||||
public OAuth2TokenRevocationAuthenticationToken(OAuth2Token revokedToken,
|
||||
Authentication clientPrincipal) {
|
||||
public OAuth2TokenRevocationAuthenticationToken(OAuth2Token revokedToken, Authentication clientPrincipal) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(revokedToken, "revokedToken cannot be null");
|
||||
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
|
||||
this.token = revokedToken.getTokenValue();
|
||||
this.clientPrincipal = clientPrincipal;
|
||||
this.tokenTypeHint = null;
|
||||
setAuthenticated(true); // Indicates that the token was authenticated and revoked
|
||||
setAuthenticated(true); // Indicates that the token was authenticated and revoked
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -85,7 +88,6 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica
|
||||
|
||||
/**
|
||||
* Returns the token.
|
||||
*
|
||||
* @return the token
|
||||
*/
|
||||
public String getToken() {
|
||||
@@ -94,11 +96,11 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica
|
||||
|
||||
/**
|
||||
* Returns the token type hint.
|
||||
*
|
||||
* @return the token type hint
|
||||
*/
|
||||
@Nullable
|
||||
public String getTokenTypeHint() {
|
||||
return this.tokenTypeHint;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,8 +33,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Public Client Authentication,
|
||||
* which authenticates the {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter.
|
||||
* An {@link AuthenticationProvider} implementation used for OAuth 2.0 Public Client
|
||||
* Authentication, which authenticates the {@link PkceParameterNames#CODE_VERIFIER
|
||||
* code_verifier} parameter.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.2.3
|
||||
@@ -44,14 +45,18 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2AuthorizationService
|
||||
*/
|
||||
public final class PublicClientAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final CodeVerifierAuthenticator codeVerifierAuthenticator;
|
||||
|
||||
/**
|
||||
* Constructs a {@code PublicClientAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs a {@code PublicClientAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
@@ -65,8 +70,7 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OAuth2ClientAuthenticationToken clientAuthentication =
|
||||
(OAuth2ClientAuthenticationToken) authentication;
|
||||
OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;
|
||||
|
||||
if (!ClientAuthenticationMethod.NONE.equals(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
return null;
|
||||
@@ -82,8 +86,8 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP
|
||||
this.logger.trace("Retrieved registered client");
|
||||
}
|
||||
|
||||
if (!registeredClient.getClientAuthenticationMethods().contains(
|
||||
clientAuthentication.getClientAuthenticationMethod())) {
|
||||
if (!registeredClient.getClientAuthenticationMethods()
|
||||
.contains(clientAuthentication.getClientAuthenticationMethod())) {
|
||||
throwInvalidClient("authentication_method");
|
||||
}
|
||||
|
||||
@@ -108,11 +112,8 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP
|
||||
}
|
||||
|
||||
private static void throwInvalidClient(String parameterName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName,
|
||||
ERROR_URI
|
||||
);
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT,
|
||||
"Client authentication failed: " + parameterName, ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ import org.springframework.util.StringUtils;
|
||||
* A {@link RegisteredClientRepository} that stores {@link RegisteredClient}(s) in-memory.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This implementation is recommended ONLY to be used during development/testing.
|
||||
* <b>NOTE:</b> This implementation is recommended ONLY to be used during
|
||||
* development/testing.
|
||||
*
|
||||
* @author Anoop Garlapati
|
||||
* @author Ovidiu Popa
|
||||
@@ -38,12 +39,14 @@ import org.springframework.util.StringUtils;
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public final class InMemoryRegisteredClientRepository implements RegisteredClientRepository {
|
||||
|
||||
private final Map<String, RegisteredClient> idRegistrationMap;
|
||||
|
||||
private final Map<String, RegisteredClient> clientIdRegistrationMap;
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided
|
||||
* parameters.
|
||||
* @param registrations the client registration(s)
|
||||
*/
|
||||
public InMemoryRegisteredClientRepository(RegisteredClient... registrations) {
|
||||
@@ -51,8 +54,8 @@ public final class InMemoryRegisteredClientRepository implements RegisteredClien
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code InMemoryRegisteredClientRepository} using the provided
|
||||
* parameters.
|
||||
* @param registrations the client registration(s)
|
||||
*/
|
||||
public InMemoryRegisteredClientRepository(List<RegisteredClient> registrations) {
|
||||
@@ -93,20 +96,21 @@ public final class InMemoryRegisteredClientRepository implements RegisteredClien
|
||||
return this.clientIdRegistrationMap.get(clientId);
|
||||
}
|
||||
|
||||
private void assertUniqueIdentifiers(RegisteredClient registeredClient, Map<String, RegisteredClient> registrations) {
|
||||
private void assertUniqueIdentifiers(RegisteredClient registeredClient,
|
||||
Map<String, RegisteredClient> registrations) {
|
||||
registrations.values().forEach(registration -> {
|
||||
if (registeredClient.getId().equals(registration.getId())) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||
"Found duplicate identifier: " + registeredClient.getId());
|
||||
throw new IllegalArgumentException("Registered client must be unique. " + "Found duplicate identifier: "
|
||||
+ registeredClient.getId());
|
||||
}
|
||||
if (registeredClient.getClientId().equals(registration.getClientId())) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||
"Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
throw new IllegalArgumentException("Registered client must be unique. "
|
||||
+ "Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
}
|
||||
if (StringUtils.hasText(registeredClient.getClientSecret()) &&
|
||||
registeredClient.getClientSecret().equals(registration.getClientSecret())) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||
"Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
if (StringUtils.hasText(registeredClient.getClientSecret())
|
||||
&& registeredClient.getClientSecret().equals(registration.getClientSecret())) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. "
|
||||
+ "Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,15 +56,18 @@ import org.springframework.util.StringUtils;
|
||||
* {@link JdbcOperations} for {@link RegisteredClient} persistence.
|
||||
*
|
||||
* <p>
|
||||
* <b>IMPORTANT:</b> This {@code RegisteredClientRepository} depends on the table definition described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql" and
|
||||
* therefore MUST be defined in the database schema.
|
||||
* <b>IMPORTANT:</b> This {@code RegisteredClientRepository} depends on the table
|
||||
* definition described in
|
||||
* "classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql"
|
||||
* and therefore MUST be defined in the database schema.
|
||||
*
|
||||
* <p>
|
||||
* <b>NOTE:</b> This {@code RegisteredClientRepository} is a simplified JDBC implementation that MAY be used in a production environment.
|
||||
* However, it does have limitations as it likely won't perform well in an environment requiring high throughput.
|
||||
* The expectation is that the consuming application will provide their own implementation of {@code RegisteredClientRepository}
|
||||
* that meets the performance requirements for its deployment environment.
|
||||
* <b>NOTE:</b> This {@code RegisteredClientRepository} is a simplified JDBC
|
||||
* implementation that MAY be used in a production environment. However, it does have
|
||||
* limitations as it likely won't perform well in an environment requiring high
|
||||
* throughput. The expectation is that the consuming application will provide their own
|
||||
* implementation of {@code RegisteredClientRepository} that meets the performance
|
||||
* requirements for its deployment environment.
|
||||
*
|
||||
* @author Rafal Lewczuk
|
||||
* @author Joe Grandja
|
||||
@@ -109,7 +112,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
|
||||
private static final String PK_FILTER = "id = ?";
|
||||
|
||||
private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + " WHERE ";
|
||||
private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME
|
||||
+ " WHERE ";
|
||||
|
||||
// @formatter:off
|
||||
private static final String INSERT_REGISTERED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME
|
||||
@@ -127,12 +131,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
private static final String COUNT_REGISTERED_CLIENT_SQL = "SELECT COUNT(*) FROM " + TABLE_NAME + " WHERE ";
|
||||
|
||||
private final JdbcOperations jdbcOperations;
|
||||
|
||||
private RowMapper<RegisteredClient> registeredClientRowMapper;
|
||||
|
||||
private Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper;
|
||||
|
||||
/**
|
||||
* Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters.
|
||||
*
|
||||
* @param jdbcOperations the JDBC operations
|
||||
*/
|
||||
public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations) {
|
||||
@@ -145,17 +150,18 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
@Override
|
||||
public void save(RegisteredClient registeredClient) {
|
||||
Assert.notNull(registeredClient, "registeredClient cannot be null");
|
||||
RegisteredClient existingRegisteredClient = findBy(PK_FILTER,
|
||||
registeredClient.getId());
|
||||
RegisteredClient existingRegisteredClient = findBy(PK_FILTER, registeredClient.getId());
|
||||
if (existingRegisteredClient != null) {
|
||||
updateRegisteredClient(registeredClient);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
insertRegisteredClient(registeredClient);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateRegisteredClient(RegisteredClient registeredClient) {
|
||||
List<SqlParameterValue> parameters = new ArrayList<>(this.registeredClientParametersMapper.apply(registeredClient));
|
||||
List<SqlParameterValue> parameters = new ArrayList<>(
|
||||
this.registeredClientParametersMapper.apply(registeredClient));
|
||||
SqlParameterValue id = parameters.remove(0);
|
||||
parameters.remove(0); // remove client_id
|
||||
parameters.remove(0); // remove client_id_issued_at
|
||||
@@ -172,21 +178,17 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
}
|
||||
|
||||
private void assertUniqueIdentifiers(RegisteredClient registeredClient) {
|
||||
Integer count = this.jdbcOperations.queryForObject(
|
||||
COUNT_REGISTERED_CLIENT_SQL + "client_id = ?",
|
||||
Integer.class,
|
||||
Integer count = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + "client_id = ?", Integer.class,
|
||||
registeredClient.getClientId());
|
||||
if (count != null && count > 0) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||
"Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
throw new IllegalArgumentException("Registered client must be unique. "
|
||||
+ "Found duplicate client identifier: " + registeredClient.getClientId());
|
||||
}
|
||||
count = this.jdbcOperations.queryForObject(
|
||||
COUNT_REGISTERED_CLIENT_SQL + "client_secret = ?",
|
||||
Integer.class,
|
||||
count = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + "client_secret = ?", Integer.class,
|
||||
registeredClient.getClientSecret());
|
||||
if (count != null && count > 0) {
|
||||
throw new IllegalArgumentException("Registered client must be unique. " +
|
||||
"Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
throw new IllegalArgumentException("Registered client must be unique. "
|
||||
+ "Found duplicate client secret for identifier: " + registeredClient.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,16 +205,17 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
}
|
||||
|
||||
private RegisteredClient findBy(String filter, Object... args) {
|
||||
List<RegisteredClient> result = this.jdbcOperations.query(
|
||||
LOAD_REGISTERED_CLIENT_SQL + filter, this.registeredClientRowMapper, args);
|
||||
List<RegisteredClient> result = this.jdbcOperations.query(LOAD_REGISTERED_CLIENT_SQL + filter,
|
||||
this.registeredClientRowMapper, args);
|
||||
return !result.isEmpty() ? result.get(0) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RowMapper} used for mapping the current row in {@code java.sql.ResultSet} to {@link RegisteredClient}.
|
||||
* The default is {@link RegisteredClientRowMapper}.
|
||||
*
|
||||
* @param registeredClientRowMapper the {@link RowMapper} used for mapping the current row in {@code ResultSet} to {@link RegisteredClient}
|
||||
* Sets the {@link RowMapper} used for mapping the current row in
|
||||
* {@code java.sql.ResultSet} to {@link RegisteredClient}. The default is
|
||||
* {@link RegisteredClientRowMapper}.
|
||||
* @param registeredClientRowMapper the {@link RowMapper} used for mapping the current
|
||||
* row in {@code ResultSet} to {@link RegisteredClient}
|
||||
*/
|
||||
public final void setRegisteredClientRowMapper(RowMapper<RegisteredClient> registeredClientRowMapper) {
|
||||
Assert.notNull(registeredClientRowMapper, "registeredClientRowMapper cannot be null");
|
||||
@@ -220,12 +223,14 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Function} used for mapping {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}.
|
||||
* The default is {@link RegisteredClientParametersMapper}.
|
||||
*
|
||||
* @param registeredClientParametersMapper the {@code Function} used for mapping {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}
|
||||
* Sets the {@code Function} used for mapping {@link RegisteredClient} to a
|
||||
* {@code List} of {@link SqlParameterValue}. The default is
|
||||
* {@link RegisteredClientParametersMapper}.
|
||||
* @param registeredClientParametersMapper the {@code Function} used for mapping
|
||||
* {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}
|
||||
*/
|
||||
public final void setRegisteredClientParametersMapper(Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper) {
|
||||
public final void setRegisteredClientParametersMapper(
|
||||
Function<RegisteredClient, List<SqlParameterValue>> registeredClientParametersMapper) {
|
||||
Assert.notNull(registeredClientParametersMapper, "registeredClientParametersMapper cannot be null");
|
||||
this.registeredClientParametersMapper = registeredClientParametersMapper;
|
||||
}
|
||||
@@ -247,6 +252,7 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
* {@code java.sql.ResultSet} to {@link RegisteredClient}.
|
||||
*/
|
||||
public static class RegisteredClientRowMapper implements RowMapper<RegisteredClient> {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public RegisteredClientRowMapper() {
|
||||
@@ -260,10 +266,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
Timestamp clientIdIssuedAt = rs.getTimestamp("client_id_issued_at");
|
||||
Timestamp clientSecretExpiresAt = rs.getTimestamp("client_secret_expires_at");
|
||||
Set<String> clientAuthenticationMethods = StringUtils.commaDelimitedListToSet(rs.getString("client_authentication_methods"));
|
||||
Set<String> authorizationGrantTypes = StringUtils.commaDelimitedListToSet(rs.getString("authorization_grant_types"));
|
||||
Set<String> clientAuthenticationMethods = StringUtils
|
||||
.commaDelimitedListToSet(rs.getString("client_authentication_methods"));
|
||||
Set<String> authorizationGrantTypes = StringUtils
|
||||
.commaDelimitedListToSet(rs.getString("authorization_grant_types"));
|
||||
Set<String> redirectUris = StringUtils.commaDelimitedListToSet(rs.getString("redirect_uris"));
|
||||
Set<String> postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(rs.getString("post_logout_redirect_uris"));
|
||||
Set<String> postLogoutRedirectUris = StringUtils
|
||||
.commaDelimitedListToSet(rs.getString("post_logout_redirect_uris"));
|
||||
Set<String> clientScopes = StringUtils.commaDelimitedListToSet(rs.getString("scopes"));
|
||||
|
||||
// @formatter:off
|
||||
@@ -308,8 +317,10 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
|
||||
private Map<String, Object> parseMap(String data) {
|
||||
try {
|
||||
return this.objectMapper.readValue(data, new TypeReference<Map<String, Object>>() {});
|
||||
} catch (Exception ex) {
|
||||
return this.objectMapper.readValue(data, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
@@ -317,32 +328,40 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) {
|
||||
return AuthorizationGrantType.AUTHORIZATION_CODE;
|
||||
} else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {
|
||||
}
|
||||
else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) {
|
||||
return AuthorizationGrantType.CLIENT_CREDENTIALS;
|
||||
} else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {
|
||||
}
|
||||
else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) {
|
||||
return AuthorizationGrantType.REFRESH_TOKEN;
|
||||
}
|
||||
return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type
|
||||
// Custom authorization grant type
|
||||
return new AuthorizationGrantType(authorizationGrantType);
|
||||
}
|
||||
|
||||
private static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) {
|
||||
if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) {
|
||||
return ClientAuthenticationMethod.CLIENT_SECRET_BASIC;
|
||||
} else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {
|
||||
}
|
||||
else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) {
|
||||
return ClientAuthenticationMethod.CLIENT_SECRET_POST;
|
||||
} else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {
|
||||
}
|
||||
else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) {
|
||||
return ClientAuthenticationMethod.NONE;
|
||||
}
|
||||
return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method
|
||||
// Custom client authentication method
|
||||
return new ClientAuthenticationMethod(clientAuthenticationMethod);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The default {@code Function} that maps {@link RegisteredClient} to a
|
||||
* {@code List} of {@link SqlParameterValue}.
|
||||
* The default {@code Function} that maps {@link RegisteredClient} to a {@code List}
|
||||
* of {@link SqlParameterValue}.
|
||||
*/
|
||||
public static class RegisteredClientParametersMapper implements Function<RegisteredClient, List<SqlParameterValue>> {
|
||||
public static class RegisteredClientParametersMapper
|
||||
implements Function<RegisteredClient, List<SqlParameterValue>> {
|
||||
|
||||
private ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public RegisteredClientParametersMapper() {
|
||||
@@ -354,32 +373,39 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
|
||||
@Override
|
||||
public List<SqlParameterValue> apply(RegisteredClient registeredClient) {
|
||||
Timestamp clientIdIssuedAt = registeredClient.getClientIdIssuedAt() != null ?
|
||||
Timestamp.from(registeredClient.getClientIdIssuedAt()) : Timestamp.from(Instant.now());
|
||||
Timestamp clientIdIssuedAt = registeredClient.getClientIdIssuedAt() != null
|
||||
? Timestamp.from(registeredClient.getClientIdIssuedAt()) : Timestamp.from(Instant.now());
|
||||
|
||||
Timestamp clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt() != null ?
|
||||
Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null;
|
||||
Timestamp clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt() != null
|
||||
? Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null;
|
||||
|
||||
List<String> clientAuthenticationMethods = new ArrayList<>(registeredClient.getClientAuthenticationMethods().size());
|
||||
registeredClient.getClientAuthenticationMethods().forEach(clientAuthenticationMethod ->
|
||||
clientAuthenticationMethods.add(clientAuthenticationMethod.getValue()));
|
||||
List<String> clientAuthenticationMethods = new ArrayList<>(
|
||||
registeredClient.getClientAuthenticationMethods().size());
|
||||
registeredClient.getClientAuthenticationMethods()
|
||||
.forEach(clientAuthenticationMethod -> clientAuthenticationMethods
|
||||
.add(clientAuthenticationMethod.getValue()));
|
||||
|
||||
List<String> authorizationGrantTypes = new ArrayList<>(registeredClient.getAuthorizationGrantTypes().size());
|
||||
registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType ->
|
||||
authorizationGrantTypes.add(authorizationGrantType.getValue()));
|
||||
List<String> authorizationGrantTypes = new ArrayList<>(
|
||||
registeredClient.getAuthorizationGrantTypes().size());
|
||||
registeredClient.getAuthorizationGrantTypes()
|
||||
.forEach(authorizationGrantType -> authorizationGrantTypes.add(authorizationGrantType.getValue()));
|
||||
|
||||
return Arrays.asList(
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),
|
||||
return Arrays.asList(new SqlParameterValue(Types.VARCHAR, registeredClient.getId()),
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientId()),
|
||||
new SqlParameterValue(Types.TIMESTAMP, clientIdIssuedAt),
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientSecret()),
|
||||
new SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt),
|
||||
new SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()),
|
||||
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)),
|
||||
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)),
|
||||
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),
|
||||
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())),
|
||||
new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),
|
||||
new SqlParameterValue(Types.VARCHAR,
|
||||
StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)),
|
||||
new SqlParameterValue(Types.VARCHAR,
|
||||
StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)),
|
||||
new SqlParameterValue(Types.VARCHAR,
|
||||
StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())),
|
||||
new SqlParameterValue(Types.VARCHAR,
|
||||
StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())),
|
||||
new SqlParameterValue(Types.VARCHAR,
|
||||
StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())),
|
||||
new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().getSettings())),
|
||||
new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().getSettings())));
|
||||
}
|
||||
@@ -396,7 +422,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor
|
||||
private String writeMap(Map<String, Object> data) {
|
||||
try {
|
||||
return this.objectMapper.writeValueAsString(data);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,23 +40,38 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Anoop Garlapati
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-2">Section 2 Client Registration</a>
|
||||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-2">Section 2
|
||||
* Client Registration</a>
|
||||
* @since 0.0.1
|
||||
*/
|
||||
public class RegisteredClient implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String id;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private Instant clientIdIssuedAt;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private Instant clientSecretExpiresAt;
|
||||
|
||||
private String clientName;
|
||||
|
||||
private Set<ClientAuthenticationMethod> clientAuthenticationMethods;
|
||||
|
||||
private Set<AuthorizationGrantType> authorizationGrantTypes;
|
||||
|
||||
private Set<String> redirectUris;
|
||||
|
||||
private Set<String> postLogoutRedirectUris;
|
||||
|
||||
private Set<String> scopes;
|
||||
|
||||
private ClientSettings clientSettings;
|
||||
|
||||
private TokenSettings tokenSettings;
|
||||
|
||||
protected RegisteredClient() {
|
||||
@@ -64,7 +79,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the identifier for the registration.
|
||||
*
|
||||
* @return the identifier for the registration
|
||||
*/
|
||||
public String getId() {
|
||||
@@ -73,7 +87,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the client identifier.
|
||||
*
|
||||
* @return the client identifier
|
||||
*/
|
||||
public String getClientId() {
|
||||
@@ -82,7 +95,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the time at which the client identifier was issued.
|
||||
*
|
||||
* @return the time at which the client identifier was issued
|
||||
*/
|
||||
@Nullable
|
||||
@@ -92,7 +104,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the client secret or {@code null} if not available.
|
||||
*
|
||||
* @return the client secret or {@code null} if not available
|
||||
*/
|
||||
@Nullable
|
||||
@@ -101,9 +112,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which the client secret expires or {@code null} if it does not expire.
|
||||
*
|
||||
* @return the time at which the client secret expires or {@code null} if it does not expire
|
||||
* Returns the time at which the client secret expires or {@code null} if it does not
|
||||
* expire.
|
||||
* @return the time at which the client secret expires or {@code null} if it does not
|
||||
* expire
|
||||
*/
|
||||
@Nullable
|
||||
public Instant getClientSecretExpiresAt() {
|
||||
@@ -112,7 +124,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the client name.
|
||||
*
|
||||
* @return the client name
|
||||
*/
|
||||
public String getClientName() {
|
||||
@@ -120,18 +131,20 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ClientAuthenticationMethod authentication method(s)} that the client may use.
|
||||
*
|
||||
* @return the {@code Set} of {@link ClientAuthenticationMethod authentication method(s)}
|
||||
* Returns the {@link ClientAuthenticationMethod authentication method(s)} that the
|
||||
* client may use.
|
||||
* @return the {@code Set} of {@link ClientAuthenticationMethod authentication
|
||||
* method(s)}
|
||||
*/
|
||||
public Set<ClientAuthenticationMethod> getClientAuthenticationMethods() {
|
||||
return this.clientAuthenticationMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationGrantType authorization grant type(s)} that the client may use.
|
||||
*
|
||||
* @return the {@code Set} of {@link AuthorizationGrantType authorization grant type(s)}
|
||||
* Returns the {@link AuthorizationGrantType authorization grant type(s)} that the
|
||||
* client may use.
|
||||
* @return the {@code Set} of {@link AuthorizationGrantType authorization grant
|
||||
* type(s)}
|
||||
*/
|
||||
public Set<AuthorizationGrantType> getAuthorizationGrantTypes() {
|
||||
return this.authorizationGrantTypes;
|
||||
@@ -139,7 +152,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the redirect URI(s) that the client may use in redirect-based flows.
|
||||
*
|
||||
* @return the {@code Set} of redirect URI(s)
|
||||
*/
|
||||
public Set<String> getRedirectUris() {
|
||||
@@ -147,10 +159,9 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the post logout redirect URI(s) that the client may use for logout.
|
||||
* The {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* Returns the post logout redirect URI(s) that the client may use for logout. The
|
||||
* {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
*
|
||||
* @return the {@code Set} of post logout redirect URI(s)
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -160,7 +171,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the scope(s) that the client may use.
|
||||
*
|
||||
* @return the {@code Set} of scope(s)
|
||||
*/
|
||||
public Set<String> getScopes() {
|
||||
@@ -169,7 +179,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link ClientSettings client configuration settings}.
|
||||
*
|
||||
* @return the {@link ClientSettings}
|
||||
*/
|
||||
public ClientSettings getClientSettings() {
|
||||
@@ -178,7 +187,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the {@link TokenSettings token configuration settings}.
|
||||
*
|
||||
* @return the {@link TokenSettings}
|
||||
*/
|
||||
public TokenSettings getTokenSettings() {
|
||||
@@ -194,47 +202,39 @@ public class RegisteredClient implements Serializable {
|
||||
return false;
|
||||
}
|
||||
RegisteredClient that = (RegisteredClient) obj;
|
||||
return Objects.equals(this.id, that.id) &&
|
||||
Objects.equals(this.clientId, that.clientId) &&
|
||||
Objects.equals(this.clientIdIssuedAt, that.clientIdIssuedAt) &&
|
||||
Objects.equals(this.clientSecret, that.clientSecret) &&
|
||||
Objects.equals(this.clientSecretExpiresAt, that.clientSecretExpiresAt) &&
|
||||
Objects.equals(this.clientName, that.clientName) &&
|
||||
Objects.equals(this.clientAuthenticationMethods, that.clientAuthenticationMethods) &&
|
||||
Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes) &&
|
||||
Objects.equals(this.redirectUris, that.redirectUris) &&
|
||||
Objects.equals(this.postLogoutRedirectUris, that.postLogoutRedirectUris) &&
|
||||
Objects.equals(this.scopes, that.scopes) &&
|
||||
Objects.equals(this.clientSettings, that.clientSettings) &&
|
||||
Objects.equals(this.tokenSettings, that.tokenSettings);
|
||||
return Objects.equals(this.id, that.id) && Objects.equals(this.clientId, that.clientId)
|
||||
&& Objects.equals(this.clientIdIssuedAt, that.clientIdIssuedAt)
|
||||
&& Objects.equals(this.clientSecret, that.clientSecret)
|
||||
&& Objects.equals(this.clientSecretExpiresAt, that.clientSecretExpiresAt)
|
||||
&& Objects.equals(this.clientName, that.clientName)
|
||||
&& Objects.equals(this.clientAuthenticationMethods, that.clientAuthenticationMethods)
|
||||
&& Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes)
|
||||
&& Objects.equals(this.redirectUris, that.redirectUris)
|
||||
&& Objects.equals(this.postLogoutRedirectUris, that.postLogoutRedirectUris)
|
||||
&& Objects.equals(this.scopes, that.scopes) && Objects.equals(this.clientSettings, that.clientSettings)
|
||||
&& Objects.equals(this.tokenSettings, that.tokenSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret, this.clientSecretExpiresAt,
|
||||
this.clientName, this.clientAuthenticationMethods, this.authorizationGrantTypes, this.redirectUris,
|
||||
this.postLogoutRedirectUris, this.scopes, this.clientSettings, this.tokenSettings);
|
||||
return Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret,
|
||||
this.clientSecretExpiresAt, this.clientName, this.clientAuthenticationMethods,
|
||||
this.authorizationGrantTypes, this.redirectUris, this.postLogoutRedirectUris, this.scopes,
|
||||
this.clientSettings, this.tokenSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegisteredClient {" +
|
||||
"id='" + this.id + '\'' +
|
||||
", clientId='" + this.clientId + '\'' +
|
||||
", clientName='" + this.clientName + '\'' +
|
||||
", clientAuthenticationMethods=" + this.clientAuthenticationMethods +
|
||||
", authorizationGrantTypes=" + this.authorizationGrantTypes +
|
||||
", redirectUris=" + this.redirectUris +
|
||||
", postLogoutRedirectUris=" + this.postLogoutRedirectUris +
|
||||
", scopes=" + this.scopes +
|
||||
", clientSettings=" + this.clientSettings +
|
||||
", tokenSettings=" + this.tokenSettings +
|
||||
'}';
|
||||
return "RegisteredClient {" + "id='" + this.id + '\'' + ", clientId='" + this.clientId + '\'' + ", clientName='"
|
||||
+ this.clientName + '\'' + ", clientAuthenticationMethods=" + this.clientAuthenticationMethods
|
||||
+ ", authorizationGrantTypes=" + this.authorizationGrantTypes + ", redirectUris=" + this.redirectUris
|
||||
+ ", postLogoutRedirectUris=" + this.postLogoutRedirectUris + ", scopes=" + this.scopes
|
||||
+ ", clientSettings=" + this.clientSettings + ", tokenSettings=" + this.tokenSettings + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the provided registration identifier.
|
||||
*
|
||||
* Returns a new {@link Builder}, initialized with the provided registration
|
||||
* identifier.
|
||||
* @param id the identifier for the registration
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -244,9 +244,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided {@link RegisteredClient}.
|
||||
*
|
||||
* @param registeredClient the {@link RegisteredClient} used for initializing the {@link Builder}
|
||||
* Returns a new {@link Builder}, initialized with the values from the provided
|
||||
* {@link RegisteredClient}.
|
||||
* @param registeredClient the {@link RegisteredClient} used for initializing the
|
||||
* {@link Builder}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder from(RegisteredClient registeredClient) {
|
||||
@@ -258,19 +259,33 @@ public class RegisteredClient implements Serializable {
|
||||
* A builder for {@link RegisteredClient}.
|
||||
*/
|
||||
public static class Builder implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private String id;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private Instant clientIdIssuedAt;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private Instant clientSecretExpiresAt;
|
||||
|
||||
private String clientName;
|
||||
|
||||
private final Set<ClientAuthenticationMethod> clientAuthenticationMethods = new HashSet<>();
|
||||
|
||||
private final Set<AuthorizationGrantType> authorizationGrantTypes = new HashSet<>();
|
||||
|
||||
private final Set<String> redirectUris = new HashSet<>();
|
||||
|
||||
private final Set<String> postLogoutRedirectUris = new HashSet<>();
|
||||
|
||||
private final Set<String> scopes = new HashSet<>();
|
||||
|
||||
private ClientSettings clientSettings;
|
||||
|
||||
private TokenSettings tokenSettings;
|
||||
|
||||
protected Builder(String id) {
|
||||
@@ -299,13 +314,13 @@ public class RegisteredClient implements Serializable {
|
||||
if (!CollectionUtils.isEmpty(registeredClient.getScopes())) {
|
||||
this.scopes.addAll(registeredClient.getScopes());
|
||||
}
|
||||
this.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings()).build();
|
||||
this.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings())
|
||||
.build();
|
||||
this.tokenSettings = TokenSettings.withSettings(registeredClient.getTokenSettings().getSettings()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the identifier for the registration.
|
||||
*
|
||||
* @param id the identifier for the registration
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -316,7 +331,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the client identifier.
|
||||
*
|
||||
* @param clientId the client identifier
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -327,7 +341,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the time at which the client identifier was issued.
|
||||
*
|
||||
* @param clientIdIssuedAt the time at which the client identifier was issued
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -338,7 +351,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the client secret.
|
||||
*
|
||||
* @param clientSecret the client secret
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -348,9 +360,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time at which the client secret expires or {@code null} if it does not expire.
|
||||
*
|
||||
* @param clientSecretExpiresAt the time at which the client secret expires or {@code null} if it does not expire
|
||||
* Sets the time at which the client secret expires or {@code null} if it does not
|
||||
* expire.
|
||||
* @param clientSecretExpiresAt the time at which the client secret expires or
|
||||
* {@code null} if it does not expire
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder clientSecretExpiresAt(Instant clientSecretExpiresAt) {
|
||||
@@ -360,7 +373,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the client name.
|
||||
*
|
||||
* @param clientName the client name
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -370,9 +382,8 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link ClientAuthenticationMethod authentication method}
|
||||
* the client may use when authenticating with the authorization server.
|
||||
*
|
||||
* Adds an {@link ClientAuthenticationMethod authentication method} the client may
|
||||
* use when authenticating with the authorization server.
|
||||
* @param clientAuthenticationMethod the authentication method
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -382,10 +393,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the {@link ClientAuthenticationMethod authentication method(s)}
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param clientAuthenticationMethodsConsumer a {@code Consumer} of the authentication method(s)
|
||||
* A {@code Consumer} of the {@link ClientAuthenticationMethod authentication
|
||||
* method(s)} allowing the ability to add, replace, or remove.
|
||||
* @param clientAuthenticationMethodsConsumer a {@code Consumer} of the
|
||||
* authentication method(s)
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder clientAuthenticationMethods(
|
||||
@@ -395,8 +406,8 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthorizationGrantType authorization grant type} the client may use.
|
||||
*
|
||||
* Adds an {@link AuthorizationGrantType authorization grant type} the client may
|
||||
* use.
|
||||
* @param authorizationGrantType the authorization grant type
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -406,10 +417,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the {@link AuthorizationGrantType authorization grant type(s)}
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param authorizationGrantTypesConsumer a {@code Consumer} of the authorization grant type(s)
|
||||
* A {@code Consumer} of the {@link AuthorizationGrantType authorization grant
|
||||
* type(s)} allowing the ability to add, replace, or remove.
|
||||
* @param authorizationGrantTypesConsumer a {@code Consumer} of the authorization
|
||||
* grant type(s)
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public Builder authorizationGrantTypes(Consumer<Set<AuthorizationGrantType>> authorizationGrantTypesConsumer) {
|
||||
@@ -419,7 +430,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Adds a redirect URI the client may use in a redirect-based flow.
|
||||
*
|
||||
* @param redirectUri the redirect URI
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -429,9 +439,8 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the redirect URI(s)
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* A {@code Consumer} of the redirect URI(s) allowing the ability to add, replace,
|
||||
* or remove.
|
||||
* @param redirectUrisConsumer a {@link Consumer} of the redirect URI(s)
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -441,10 +450,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a post logout redirect URI the client may use for logout.
|
||||
* The {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
*
|
||||
* Adds a post logout redirect URI the client may use for logout. The
|
||||
* {@code post_logout_redirect_uri} parameter is used by the client when
|
||||
* requesting that the End-User's User Agent be redirected to after a logout has
|
||||
* been performed.
|
||||
* @param postLogoutRedirectUri the post logout redirect URI
|
||||
* @return the {@link Builder}
|
||||
* @since 1.1
|
||||
@@ -455,10 +464,10 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the post logout redirect URI(s)
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param postLogoutRedirectUrisConsumer a {@link Consumer} of the post logout redirect URI(s)
|
||||
* A {@code Consumer} of the post logout redirect URI(s) allowing the ability to
|
||||
* add, replace, or remove.
|
||||
* @param postLogoutRedirectUrisConsumer a {@link Consumer} of the post logout
|
||||
* redirect URI(s)
|
||||
* @return the {@link Builder}
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -469,7 +478,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Adds a scope the client may use.
|
||||
*
|
||||
* @param scope the scope
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -479,9 +487,8 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the scope(s)
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* A {@code Consumer} of the scope(s) allowing the ability to add, replace, or
|
||||
* remove.
|
||||
* @param scopesConsumer a {@link Consumer} of the scope(s)
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -492,7 +499,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@link ClientSettings client configuration settings}.
|
||||
*
|
||||
* @param clientSettings the client configuration settings
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -503,7 +509,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the {@link TokenSettings token configuration settings}.
|
||||
*
|
||||
* @param tokenSettings the token configuration settings
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -514,7 +519,6 @@ public class RegisteredClient implements Serializable {
|
||||
|
||||
/**
|
||||
* Builds a new {@link RegisteredClient}.
|
||||
*
|
||||
* @return a {@link RegisteredClient}
|
||||
*/
|
||||
public RegisteredClient build() {
|
||||
@@ -550,9 +554,9 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
private boolean isPublicClientType() {
|
||||
return this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE) &&
|
||||
this.clientAuthenticationMethods.size() == 1 &&
|
||||
this.clientAuthenticationMethods.contains(ClientAuthenticationMethod.NONE);
|
||||
return this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE)
|
||||
&& this.clientAuthenticationMethods.size() == 1
|
||||
&& this.clientAuthenticationMethods.contains(ClientAuthenticationMethod.NONE);
|
||||
}
|
||||
|
||||
private RegisteredClient create() {
|
||||
@@ -564,16 +568,14 @@ public class RegisteredClient implements Serializable {
|
||||
registeredClient.clientSecret = this.clientSecret;
|
||||
registeredClient.clientSecretExpiresAt = this.clientSecretExpiresAt;
|
||||
registeredClient.clientName = this.clientName;
|
||||
registeredClient.clientAuthenticationMethods = Collections.unmodifiableSet(
|
||||
new HashSet<>(this.clientAuthenticationMethods));
|
||||
registeredClient.authorizationGrantTypes = Collections.unmodifiableSet(
|
||||
new HashSet<>(this.authorizationGrantTypes));
|
||||
registeredClient.redirectUris = Collections.unmodifiableSet(
|
||||
new HashSet<>(this.redirectUris));
|
||||
registeredClient.postLogoutRedirectUris = Collections.unmodifiableSet(
|
||||
new HashSet<>(this.postLogoutRedirectUris));
|
||||
registeredClient.scopes = Collections.unmodifiableSet(
|
||||
new HashSet<>(this.scopes));
|
||||
registeredClient.clientAuthenticationMethods = Collections
|
||||
.unmodifiableSet(new HashSet<>(this.clientAuthenticationMethods));
|
||||
registeredClient.authorizationGrantTypes = Collections
|
||||
.unmodifiableSet(new HashSet<>(this.authorizationGrantTypes));
|
||||
registeredClient.redirectUris = Collections.unmodifiableSet(new HashSet<>(this.redirectUris));
|
||||
registeredClient.postLogoutRedirectUris = Collections
|
||||
.unmodifiableSet(new HashSet<>(this.postLogoutRedirectUris));
|
||||
registeredClient.scopes = Collections.unmodifiableSet(new HashSet<>(this.scopes));
|
||||
registeredClient.clientSettings = this.clientSettings;
|
||||
registeredClient.tokenSettings = this.tokenSettings;
|
||||
|
||||
@@ -591,10 +593,9 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
private static boolean validateScope(String scope) {
|
||||
return scope == null ||
|
||||
scope.chars().allMatch(c -> withinTheRangeOf(c, 0x21, 0x21) ||
|
||||
withinTheRangeOf(c, 0x23, 0x5B) ||
|
||||
withinTheRangeOf(c, 0x5D, 0x7E));
|
||||
return scope == null || scope.chars()
|
||||
.allMatch(c -> withinTheRangeOf(c, 0x21, 0x21) || withinTheRangeOf(c, 0x23, 0x5B)
|
||||
|| withinTheRangeOf(c, 0x5D, 0x7E));
|
||||
}
|
||||
|
||||
private static boolean withinTheRangeOf(int c, int min, int max) {
|
||||
@@ -618,8 +619,8 @@ public class RegisteredClient implements Serializable {
|
||||
}
|
||||
|
||||
for (String postLogoutRedirectUri : this.postLogoutRedirectUris) {
|
||||
Assert.isTrue(validateRedirectUri(postLogoutRedirectUri),
|
||||
"post_logout_redirect_uri \"" + postLogoutRedirectUri + "\" is not a valid post logout redirect URI or contains fragment");
|
||||
Assert.isTrue(validateRedirectUri(postLogoutRedirectUri), "post_logout_redirect_uri \""
|
||||
+ postLogoutRedirectUri + "\" is not a valid post logout redirect URI or contains fragment");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,10 +628,12 @@ public class RegisteredClient implements Serializable {
|
||||
try {
|
||||
URI validRedirectUri = new URI(redirectUri);
|
||||
return validRedirectUri.getFragment() == null;
|
||||
} catch (URISyntaxException ex) {
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,16 +32,15 @@ public interface RegisteredClientRepository {
|
||||
* Saves the registered client.
|
||||
*
|
||||
* <p>
|
||||
* IMPORTANT: Sensitive information should be encoded externally from the implementation, e.g. {@link RegisteredClient#getClientSecret()}
|
||||
*
|
||||
* IMPORTANT: Sensitive information should be encoded externally from the
|
||||
* implementation, e.g. {@link RegisteredClient#getClientSecret()}
|
||||
* @param registeredClient the {@link RegisteredClient}
|
||||
*/
|
||||
void save(RegisteredClient registeredClient);
|
||||
|
||||
/**
|
||||
* Returns the registered client identified by the provided {@code id},
|
||||
* or {@code null} if not found.
|
||||
*
|
||||
* Returns the registered client identified by the provided {@code id}, or
|
||||
* {@code null} if not found.
|
||||
* @param id the registration identifier
|
||||
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||
*/
|
||||
@@ -49,9 +48,8 @@ public interface RegisteredClientRepository {
|
||||
RegisteredClient findById(String id);
|
||||
|
||||
/**
|
||||
* Returns the registered client identified by the provided {@code clientId},
|
||||
* or {@code null} if not found.
|
||||
*
|
||||
* Returns the registered client identified by the provided {@code clientId}, or
|
||||
* {@code null} if not found.
|
||||
* @param clientId the client identifier
|
||||
* @return the {@link RegisteredClient} if found, otherwise {@code null}
|
||||
*/
|
||||
|
||||
@@ -78,10 +78,10 @@ public class OAuth2AuthorizationServerConfiguration {
|
||||
jwsAlgs.addAll(JWSAlgorithm.Family.EC);
|
||||
jwsAlgs.addAll(JWSAlgorithm.Family.HMAC_SHA);
|
||||
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
|
||||
JWSKeySelector<SecurityContext> jwsKeySelector =
|
||||
new JWSVerificationKeySelector<>(jwsAlgs, jwkSource);
|
||||
JWSKeySelector<SecurityContext> jwsKeySelector = new JWSVerificationKeySelector<>(jwsAlgs, jwkSource);
|
||||
jwtProcessor.setJWSKeySelector(jwsKeySelector);
|
||||
// Override the default Nimbus claims set verifier as NimbusJwtDecoder handles it instead
|
||||
// Override the default Nimbus claims set verifier as NimbusJwtDecoder handles it
|
||||
// instead
|
||||
jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> {
|
||||
});
|
||||
return new NimbusJwtDecoder(jwtProcessor);
|
||||
@@ -90,7 +90,8 @@ public class OAuth2AuthorizationServerConfiguration {
|
||||
@Bean
|
||||
RegisterMissingBeanPostProcessor registerMissingBeanPostProcessor() {
|
||||
RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor();
|
||||
postProcessor.addBeanDefinition(AuthorizationServerSettings.class, () -> AuthorizationServerSettings.builder().build());
|
||||
postProcessor.addBeanDefinition(AuthorizationServerSettings.class,
|
||||
() -> AuthorizationServerSettings.builder().build());
|
||||
return postProcessor;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,14 +33,18 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
|
||||
|
||||
/**
|
||||
* Post processor to register one or more bean definitions on container initialization, if not already present.
|
||||
* Post processor to register one or more bean definitions on container initialization, if
|
||||
* not already present.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 0.2.0
|
||||
*/
|
||||
final class RegisterMissingBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware {
|
||||
|
||||
private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
|
||||
private final List<AbstractBeanDefinition> beanDefinitions = new ArrayList<>();
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* @since 0.1.2
|
||||
*/
|
||||
abstract class AbstractOAuth2Configurer {
|
||||
|
||||
private final ObjectPostProcessor<Object> objectPostProcessor;
|
||||
|
||||
AbstractOAuth2Configurer(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
|
||||
@@ -32,7 +32,8 @@ import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* A {@code Filter} that associates the {@link AuthorizationServerContext} to the {@link AuthorizationServerContextHolder}.
|
||||
* A {@code Filter} that associates the {@link AuthorizationServerContext} to the
|
||||
* {@link AuthorizationServerContextHolder}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.2.2
|
||||
@@ -41,6 +42,7 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* @see AuthorizationServerSettings
|
||||
*/
|
||||
final class AuthorizationServerContextFilter extends OncePerRequestFilter {
|
||||
|
||||
private final AuthorizationServerSettings authorizationServerSettings;
|
||||
|
||||
AuthorizationServerContextFilter(AuthorizationServerSettings authorizationServerSettings) {
|
||||
@@ -53,21 +55,20 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
|
||||
throws ServletException, IOException {
|
||||
|
||||
try {
|
||||
AuthorizationServerContext authorizationServerContext =
|
||||
new DefaultAuthorizationServerContext(
|
||||
() -> resolveIssuer(this.authorizationServerSettings, request),
|
||||
this.authorizationServerSettings);
|
||||
AuthorizationServerContext authorizationServerContext = new DefaultAuthorizationServerContext(
|
||||
() -> resolveIssuer(this.authorizationServerSettings, request), this.authorizationServerSettings);
|
||||
AuthorizationServerContextHolder.setContext(authorizationServerContext);
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
AuthorizationServerContextHolder.resetContext();
|
||||
}
|
||||
}
|
||||
|
||||
private static String resolveIssuer(AuthorizationServerSettings authorizationServerSettings, HttpServletRequest request) {
|
||||
return authorizationServerSettings.getIssuer() != null ?
|
||||
authorizationServerSettings.getIssuer() :
|
||||
getContextPath(request);
|
||||
private static String resolveIssuer(AuthorizationServerSettings authorizationServerSettings,
|
||||
HttpServletRequest request) {
|
||||
return authorizationServerSettings.getIssuer() != null ? authorizationServerSettings.getIssuer()
|
||||
: getContextPath(request);
|
||||
}
|
||||
|
||||
private static String getContextPath(HttpServletRequest request) {
|
||||
@@ -82,10 +83,13 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
private static final class DefaultAuthorizationServerContext implements AuthorizationServerContext {
|
||||
|
||||
private final Supplier<String> issuerSupplier;
|
||||
|
||||
private final AuthorizationServerSettings authorizationServerSettings;
|
||||
|
||||
private DefaultAuthorizationServerContext(Supplier<String> issuerSupplier, AuthorizationServerSettings authorizationServerSettings) {
|
||||
private DefaultAuthorizationServerContext(Supplier<String> issuerSupplier,
|
||||
AuthorizationServerSettings authorizationServerSettings) {
|
||||
this.issuerSupplier = issuerSupplier;
|
||||
this.authorizationServerSettings = authorizationServerSettings;
|
||||
}
|
||||
|
||||
@@ -60,15 +60,28 @@ import org.springframework.util.StringUtils;
|
||||
* @see OAuth2AuthorizationEndpointFilter
|
||||
*/
|
||||
public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> authorizationRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer = (authorizationRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> authorizationRequestConvertersConsumer = (
|
||||
authorizationRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler authorizationResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
private String consentPage;
|
||||
|
||||
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidator;
|
||||
|
||||
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
|
||||
|
||||
/**
|
||||
@@ -79,25 +92,31 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or {@link OAuth2AuthorizationConsentAuthenticationToken}
|
||||
* used for authenticating the request.
|
||||
*
|
||||
* @param authorizationRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest}
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an
|
||||
* Authorization Request (or Consent) from {@link HttpServletRequest} to an instance
|
||||
* of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or
|
||||
* {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the
|
||||
* request.
|
||||
* @param authorizationRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract an Authorization Request (or Consent) from
|
||||
* {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(AuthenticationConverter authorizationRequestConverter) {
|
||||
public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(
|
||||
AuthenticationConverter authorizationRequestConverter) {
|
||||
Assert.notNull(authorizationRequestConverter, "authorizationRequestConverter cannot be null");
|
||||
this.authorizationRequestConverters.add(authorizationRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param authorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param authorizationRequestConvertersConsumer the {@code Consumer} providing access
|
||||
* to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -109,9 +128,10 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
@@ -121,11 +141,12 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -137,56 +158,62 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* and returning the {@link OAuth2AuthorizationResponse Authorization Response}.
|
||||
*
|
||||
* @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and returning the
|
||||
* {@link OAuth2AuthorizationResponse Authorization Response}.
|
||||
* @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used
|
||||
* for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationEndpointConfigurer authorizationResponseHandler(AuthenticationSuccessHandler authorizationResponseHandler) {
|
||||
public OAuth2AuthorizationEndpointConfigurer authorizationResponseHandler(
|
||||
AuthenticationSuccessHandler authorizationResponseHandler) {
|
||||
this.authorizationResponseHandler = authorizationResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the
|
||||
* {@link OAuth2Error Error Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException}
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2AuthorizationEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the URI to redirect Resource Owners to if consent is required during
|
||||
* the {@code authorization_code} flow. A default consent page will be generated when
|
||||
* this attribute is not specified.
|
||||
* Specify the URI to redirect Resource Owners to if consent is required during the
|
||||
* {@code authorization_code} flow. A default consent page will be generated when this
|
||||
* attribute is not specified.
|
||||
*
|
||||
* If a URI is specified, applications are required to process the specified URI to generate
|
||||
* a consent page. The query string will contain the following parameters:
|
||||
* If a URI is specified, applications are required to process the specified URI to
|
||||
* generate a consent page. The query string will contain the following parameters:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code client_id} - the client identifier</li>
|
||||
* <li>{@code scope} - a space-delimited list of scopes present in the authorization request</li>
|
||||
* <li>{@code scope} - a space-delimited list of scopes present in the authorization
|
||||
* request</li>
|
||||
* <li>{@code state} - a CSRF protection token</li>
|
||||
* </ul>
|
||||
*
|
||||
* In general, the consent page should create a form that submits
|
||||
* a request with the following requirements:
|
||||
* In general, the consent page should create a form that submits a request with the
|
||||
* following requirements:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It must be an HTTP POST</li>
|
||||
* <li>It must be submitted to {@link AuthorizationServerSettings#getAuthorizationEndpoint()}</li>
|
||||
* <li>It must be submitted to
|
||||
* {@link AuthorizationServerSettings#getAuthorizationEndpoint()}</li>
|
||||
* <li>It must include the received {@code client_id} as an HTTP parameter</li>
|
||||
* <li>It must include the received {@code state} as an HTTP parameter</li>
|
||||
* <li>It must include the list of {@code scope}s the {@code Resource Owner}
|
||||
* consented to as an HTTP parameter</li>
|
||||
* <li>It must include the list of {@code scope}s the {@code Resource Owner} consented
|
||||
* to as an HTTP parameter</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent")
|
||||
* @param consentPage the URI of the custom consent page to redirect to if consent is
|
||||
* required (e.g. "/oauth2/consent")
|
||||
* @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationEndpointConfigurer consentPage(String consentPage) {
|
||||
@@ -196,10 +223,9 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
|
||||
void addAuthorizationCodeRequestAuthenticationValidator(
|
||||
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authenticationValidator) {
|
||||
this.authorizationCodeRequestAuthenticationValidator =
|
||||
this.authorizationCodeRequestAuthenticationValidator == null ?
|
||||
authenticationValidator :
|
||||
this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator);
|
||||
this.authorizationCodeRequestAuthenticationValidator = this.authorizationCodeRequestAuthenticationValidator == null
|
||||
? authenticationValidator
|
||||
: this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator);
|
||||
}
|
||||
|
||||
void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) {
|
||||
@@ -208,13 +234,12 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getAuthorizationEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getAuthorizationEndpoint(),
|
||||
HttpMethod.GET.name()),
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getAuthorizationEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getAuthorizationEndpoint(),
|
||||
HttpMethod.POST.name()));
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
@@ -222,26 +247,25 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter =
|
||||
new OAuth2AuthorizationEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getAuthorizationEndpoint());
|
||||
OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getAuthorizationEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.authorizationRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.authorizationRequestConverters);
|
||||
}
|
||||
this.authorizationRequestConvertersConsumer.accept(authenticationConverters);
|
||||
authorizationEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
authorizationEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.authorizationResponseHandler != null) {
|
||||
authorizationEndpointFilter.setAuthenticationSuccessHandler(this.authorizationResponseHandler);
|
||||
}
|
||||
@@ -254,7 +278,8 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
if (this.sessionAuthenticationStrategy != null) {
|
||||
authorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);
|
||||
}
|
||||
httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -274,23 +299,21 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
|
||||
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider =
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));
|
||||
OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));
|
||||
if (this.authorizationCodeRequestAuthenticationValidator != null) {
|
||||
authorizationCodeRequestAuthenticationProvider.setAuthenticationValidator(
|
||||
new OAuth2AuthorizationCodeRequestAuthenticationValidator()
|
||||
.andThen(this.authorizationCodeRequestAuthenticationValidator));
|
||||
authorizationCodeRequestAuthenticationProvider
|
||||
.setAuthenticationValidator(new OAuth2AuthorizationCodeRequestAuthenticationValidator()
|
||||
.andThen(this.authorizationCodeRequestAuthenticationValidator));
|
||||
}
|
||||
authenticationProviders.add(authorizationCodeRequestAuthenticationProvider);
|
||||
|
||||
OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider =
|
||||
new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));
|
||||
OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity));
|
||||
authenticationProviders.add(authorizationConsentAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -84,16 +84,16 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
extends AbstractHttpConfigurer<OAuth2AuthorizationServerConfigurer, HttpSecurity> {
|
||||
|
||||
private final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = createConfigurers();
|
||||
private RequestMatcher endpointsMatcher;
|
||||
|
||||
private RequestMatcher endpointsMatcher;
|
||||
|
||||
/**
|
||||
* Sets the repository of registered clients.
|
||||
*
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer registeredClientRepository(RegisteredClientRepository registeredClientRepository) {
|
||||
public OAuth2AuthorizationServerConfigurer registeredClientRepository(
|
||||
RegisteredClientRepository registeredClientRepository) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
|
||||
return this;
|
||||
@@ -101,7 +101,6 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Sets the authorization service.
|
||||
*
|
||||
* @param authorizationService the authorization service
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
@@ -113,11 +112,11 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Sets the authorization consent service.
|
||||
*
|
||||
* @param authorizationConsentService the authorization consent service
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer authorizationConsentService(OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
public OAuth2AuthorizationServerConfigurer authorizationConsentService(
|
||||
OAuth2AuthorizationConsentService authorizationConsentService) {
|
||||
Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null");
|
||||
getBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService);
|
||||
return this;
|
||||
@@ -125,11 +124,11 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Sets the authorization server settings.
|
||||
*
|
||||
* @param authorizationServerSettings the authorization server settings
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer authorizationServerSettings(AuthorizationServerSettings authorizationServerSettings) {
|
||||
public OAuth2AuthorizationServerConfigurer authorizationServerSettings(
|
||||
AuthorizationServerSettings authorizationServerSettings) {
|
||||
Assert.notNull(authorizationServerSettings, "authorizationServerSettings cannot be null");
|
||||
getBuilder().setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings);
|
||||
return this;
|
||||
@@ -137,12 +136,12 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Sets the token generator.
|
||||
*
|
||||
* @param tokenGenerator the token generator
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer tokenGenerator(OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
public OAuth2AuthorizationServerConfigurer tokenGenerator(
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
getBuilder().setSharedObject(OAuth2TokenGenerator.class, tokenGenerator);
|
||||
return this;
|
||||
@@ -150,101 +149,111 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Configures OAuth 2.0 Client Authentication.
|
||||
*
|
||||
* @param clientAuthenticationCustomizer the {@link Customizer} providing access to the {@link OAuth2ClientAuthenticationConfigurer}
|
||||
* @param clientAuthenticationCustomizer the {@link Customizer} providing access to
|
||||
* the {@link OAuth2ClientAuthenticationConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer clientAuthentication(Customizer<OAuth2ClientAuthenticationConfigurer> clientAuthenticationCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer clientAuthentication(
|
||||
Customizer<OAuth2ClientAuthenticationConfigurer> clientAuthenticationCustomizer) {
|
||||
clientAuthenticationCustomizer.customize(getConfigurer(OAuth2ClientAuthenticationConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Authorization Server Metadata Endpoint.
|
||||
*
|
||||
* @param authorizationServerMetadataEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer}
|
||||
* @param authorizationServerMetadataEndpointCustomizer the {@link Customizer}
|
||||
* providing access to the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint(Customizer<OAuth2AuthorizationServerMetadataEndpointConfigurer> authorizationServerMetadataEndpointCustomizer) {
|
||||
authorizationServerMetadataEndpointCustomizer.customize(getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class));
|
||||
public OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint(
|
||||
Customizer<OAuth2AuthorizationServerMetadataEndpointConfigurer> authorizationServerMetadataEndpointCustomizer) {
|
||||
authorizationServerMetadataEndpointCustomizer
|
||||
.customize(getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Authorization Endpoint.
|
||||
*
|
||||
* @param authorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2AuthorizationEndpointConfigurer}
|
||||
* @param authorizationEndpointCustomizer the {@link Customizer} providing access to
|
||||
* the {@link OAuth2AuthorizationEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer authorizationEndpoint(Customizer<OAuth2AuthorizationEndpointConfigurer> authorizationEndpointCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer authorizationEndpoint(
|
||||
Customizer<OAuth2AuthorizationEndpointConfigurer> authorizationEndpointCustomizer) {
|
||||
authorizationEndpointCustomizer.customize(getConfigurer(OAuth2AuthorizationEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Token Endpoint.
|
||||
*
|
||||
* @param tokenEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenEndpointConfigurer}
|
||||
* @param tokenEndpointCustomizer the {@link Customizer} providing access to the
|
||||
* {@link OAuth2TokenEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer tokenEndpoint(Customizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer tokenEndpoint(
|
||||
Customizer<OAuth2TokenEndpointConfigurer> tokenEndpointCustomizer) {
|
||||
tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Token Introspection Endpoint.
|
||||
*
|
||||
* @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenIntrospectionEndpointConfigurer}
|
||||
* @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access
|
||||
* to the {@link OAuth2TokenIntrospectionEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(Customizer<OAuth2TokenIntrospectionEndpointConfigurer> tokenIntrospectionEndpointCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(
|
||||
Customizer<OAuth2TokenIntrospectionEndpointConfigurer> tokenIntrospectionEndpointCustomizer) {
|
||||
tokenIntrospectionEndpointCustomizer.customize(getConfigurer(OAuth2TokenIntrospectionEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Token Revocation Endpoint.
|
||||
*
|
||||
* @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenRevocationEndpointConfigurer}
|
||||
* @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to
|
||||
* the {@link OAuth2TokenRevocationEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 0.2.2
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer<OAuth2TokenRevocationEndpointConfigurer> tokenRevocationEndpointCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(
|
||||
Customizer<OAuth2TokenRevocationEndpointConfigurer> tokenRevocationEndpointCustomizer) {
|
||||
tokenRevocationEndpointCustomizer.customize(getConfigurer(OAuth2TokenRevocationEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Device Authorization Endpoint.
|
||||
*
|
||||
* @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer}
|
||||
* @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing
|
||||
* access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(Customizer<OAuth2DeviceAuthorizationEndpointConfigurer> deviceAuthorizationEndpointCustomizer) {
|
||||
deviceAuthorizationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class));
|
||||
public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(
|
||||
Customizer<OAuth2DeviceAuthorizationEndpointConfigurer> deviceAuthorizationEndpointCustomizer) {
|
||||
deviceAuthorizationEndpointCustomizer
|
||||
.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OAuth 2.0 Device Verification Endpoint.
|
||||
*
|
||||
* @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceVerificationEndpointConfigurer}
|
||||
* @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access
|
||||
* to the {@link OAuth2DeviceVerificationEndpointConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(Customizer<OAuth2DeviceVerificationEndpointConfigurer> deviceVerificationEndpointCustomizer) {
|
||||
public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(
|
||||
Customizer<OAuth2DeviceVerificationEndpointConfigurer> deviceVerificationEndpointCustomizer) {
|
||||
deviceVerificationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures OpenID Connect 1.0 support (disabled by default).
|
||||
*
|
||||
* @param oidcCustomizer the {@link Customizer} providing access to the {@link OidcConfigurer}
|
||||
* @param oidcCustomizer the {@link Customizer} providing access to the
|
||||
* {@link OidcConfigurer}
|
||||
* @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerConfigurer oidc(Customizer<OidcConfigurer> oidcCustomizer) {
|
||||
@@ -259,7 +268,6 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
/**
|
||||
* Returns a {@link RequestMatcher} for the authorization server endpoints.
|
||||
*
|
||||
* @return a {@link RequestMatcher} for the authorization server endpoints
|
||||
*/
|
||||
public RequestMatcher getEndpointsMatcher() {
|
||||
@@ -270,42 +278,45 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
@Override
|
||||
public void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
validateAuthorizationServerSettings(authorizationServerSettings);
|
||||
|
||||
if (isOidcEnabled()) {
|
||||
// Add OpenID Connect session tracking capabilities.
|
||||
initSessionRegistry(httpSecurity);
|
||||
SessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class);
|
||||
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer =
|
||||
getConfigurer(OAuth2AuthorizationEndpointConfigurer.class);
|
||||
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer(
|
||||
OAuth2AuthorizationEndpointConfigurer.class);
|
||||
authorizationEndpointConfigurer.setSessionAuthenticationStrategy((authentication, request, response) -> {
|
||||
if (authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) {
|
||||
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {
|
||||
if (sessionRegistry.getSessionInformation(request.getSession().getId()) == null) {
|
||||
sessionRegistry.registerNewSession(
|
||||
request.getSession().getId(),
|
||||
((Authentication) authorizationCodeRequestAuthentication.getPrincipal()).getPrincipal());
|
||||
sessionRegistry.registerNewSession(request.getSession().getId(),
|
||||
((Authentication) authorizationCodeRequestAuthentication.getPrincipal())
|
||||
.getPrincipal());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// OpenID Connect is disabled.
|
||||
// Add an authentication validator that rejects authentication requests.
|
||||
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer =
|
||||
getConfigurer(OAuth2AuthorizationEndpointConfigurer.class);
|
||||
authorizationEndpointConfigurer.addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication =
|
||||
authenticationContext.getAuthentication();
|
||||
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
"OpenID Connect 1.0 authentication requests are restricted.",
|
||||
"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1");
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(
|
||||
error, authorizationCodeRequestAuthentication);
|
||||
}
|
||||
});
|
||||
OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer(
|
||||
OAuth2AuthorizationEndpointConfigurer.class);
|
||||
authorizationEndpointConfigurer
|
||||
.addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> {
|
||||
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext
|
||||
.getAuthentication();
|
||||
if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE,
|
||||
"OpenID Connect 1.0 authentication requests are restricted.",
|
||||
"https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1");
|
||||
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error,
|
||||
authorizationCodeRequestAuthentication);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<RequestMatcher> requestMatchers = new ArrayList<>();
|
||||
@@ -313,20 +324,18 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
configurer.init(httpSecurity);
|
||||
requestMatchers.add(configurer.getRequestMatcher());
|
||||
});
|
||||
requestMatchers.add(new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name()));
|
||||
requestMatchers
|
||||
.add(new AntPathRequestMatcher(authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name()));
|
||||
this.endpointsMatcher = new OrRequestMatcher(requestMatchers);
|
||||
|
||||
ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = httpSecurity
|
||||
.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
if (exceptionHandling != null) {
|
||||
exceptionHandling.defaultAuthenticationEntryPointFor(
|
||||
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new OrRequestMatcher(
|
||||
getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
|
||||
exceptionHandling.defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
|
||||
new OrRequestMatcher(getRequestMatcher(OAuth2TokenEndpointConfigurer.class),
|
||||
getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class),
|
||||
getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class),
|
||||
getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class))
|
||||
);
|
||||
getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,16 +343,19 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
public void configure(HttpSecurity httpSecurity) {
|
||||
this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
|
||||
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(authorizationServerSettings);
|
||||
AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(
|
||||
authorizationServerSettings);
|
||||
httpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class);
|
||||
|
||||
JWKSource<com.nimbusds.jose.proc.SecurityContext> jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity);
|
||||
if (jwkSource != null) {
|
||||
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(
|
||||
jwkSource, authorizationServerSettings.getJwkSetEndpoint());
|
||||
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(jwkSource,
|
||||
authorizationServerSettings.getJwkSetEndpoint());
|
||||
httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,14 +365,21 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
|
||||
private Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> createConfigurers() {
|
||||
Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
|
||||
configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class, new OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2AuthorizationEndpointConfigurer.class, new OAuth2AuthorizationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2ClientAuthenticationConfigurer.class,
|
||||
new OAuth2ClientAuthenticationConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class,
|
||||
new OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2AuthorizationEndpointConfigurer.class,
|
||||
new OAuth2AuthorizationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class,
|
||||
new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2TokenRevocationEndpointConfigurer.class,
|
||||
new OAuth2TokenRevocationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class,
|
||||
new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess));
|
||||
configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class,
|
||||
new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess));
|
||||
return configurers;
|
||||
}
|
||||
|
||||
@@ -384,7 +403,8 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
try {
|
||||
issuerUri = new URI(authorizationServerSettings.getIssuer());
|
||||
issuerUri.toURL();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("issuer must be a valid URL", ex);
|
||||
}
|
||||
// rfc8414 https://datatracker.ietf.org/doc/html/rfc8414#section-2
|
||||
@@ -403,9 +423,10 @@ public final class OAuth2AuthorizationServerConfigurer
|
||||
httpSecurity.setSharedObject(SessionRegistry.class, sessionRegistry);
|
||||
}
|
||||
|
||||
private static void registerDelegateApplicationListener(HttpSecurity httpSecurity, ApplicationListener<?> delegate) {
|
||||
DelegatingApplicationListener delegatingApplicationListener =
|
||||
OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, DelegatingApplicationListener.class);
|
||||
private static void registerDelegateApplicationListener(HttpSecurity httpSecurity,
|
||||
ApplicationListener<?> delegate) {
|
||||
DelegatingApplicationListener delegatingApplicationListener = OAuth2ConfigurerUtils
|
||||
.getOptionalBean(httpSecurity, DelegatingApplicationListener.class);
|
||||
if (delegatingApplicationListener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,11 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* @see OAuth2AuthorizationServerMetadataEndpointFilter
|
||||
*/
|
||||
public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer;
|
||||
|
||||
private Consumer<OAuth2AuthorizationServerMetadata.Builder> defaultAuthorizationServerMetadataCustomizer;
|
||||
|
||||
/**
|
||||
@@ -47,11 +50,13 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder}
|
||||
* allowing the ability to customize the claims of the Authorization Server's configuration.
|
||||
*
|
||||
* @param authorizationServerMetadataCustomizer the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder}
|
||||
* @return the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the
|
||||
* {@link OAuth2AuthorizationServerMetadata.Builder} allowing the ability to customize
|
||||
* the claims of the Authorization Server's configuration.
|
||||
* @param authorizationServerMetadataCustomizer the {@code Consumer} providing access
|
||||
* to the {@link OAuth2AuthorizationServerMetadata.Builder}
|
||||
* @return the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataCustomizer(
|
||||
Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer) {
|
||||
@@ -61,40 +66,40 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A
|
||||
|
||||
void addDefaultAuthorizationServerMetadataCustomizer(
|
||||
Consumer<OAuth2AuthorizationServerMetadata.Builder> defaultAuthorizationServerMetadataCustomizer) {
|
||||
this.defaultAuthorizationServerMetadataCustomizer =
|
||||
this.defaultAuthorizationServerMetadataCustomizer == null ?
|
||||
defaultAuthorizationServerMetadataCustomizer :
|
||||
this.defaultAuthorizationServerMetadataCustomizer.andThen(defaultAuthorizationServerMetadataCustomizer);
|
||||
this.defaultAuthorizationServerMetadataCustomizer = this.defaultAuthorizationServerMetadataCustomizer == null
|
||||
? defaultAuthorizationServerMetadataCustomizer : this.defaultAuthorizationServerMetadataCustomizer
|
||||
.andThen(defaultAuthorizationServerMetadataCustomizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
"/.well-known/oauth-authorization-server", HttpMethod.GET.name());
|
||||
this.requestMatcher = new AntPathRequestMatcher("/.well-known/oauth-authorization-server",
|
||||
HttpMethod.GET.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
OAuth2AuthorizationServerMetadataEndpointFilter authorizationServerMetadataEndpointFilter =
|
||||
new OAuth2AuthorizationServerMetadataEndpointFilter();
|
||||
OAuth2AuthorizationServerMetadataEndpointFilter authorizationServerMetadataEndpointFilter = new OAuth2AuthorizationServerMetadataEndpointFilter();
|
||||
Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = getAuthorizationServerMetadataCustomizer();
|
||||
if (authorizationServerMetadataCustomizer != null) {
|
||||
authorizationServerMetadataEndpointFilter.setAuthorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer);
|
||||
authorizationServerMetadataEndpointFilter
|
||||
.setAuthorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer);
|
||||
}
|
||||
httpSecurity.addFilterBefore(postProcess(authorizationServerMetadataEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
httpSecurity.addFilterBefore(postProcess(authorizationServerMetadataEndpointFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
private Consumer<OAuth2AuthorizationServerMetadata.Builder> getAuthorizationServerMetadataCustomizer() {
|
||||
Consumer<OAuth2AuthorizationServerMetadata.Builder> authorizationServerMetadataCustomizer = null;
|
||||
if (this.defaultAuthorizationServerMetadataCustomizer != null || this.authorizationServerMetadataCustomizer != null) {
|
||||
if (this.defaultAuthorizationServerMetadataCustomizer != null
|
||||
|| this.authorizationServerMetadataCustomizer != null) {
|
||||
if (this.defaultAuthorizationServerMetadataCustomizer != null) {
|
||||
authorizationServerMetadataCustomizer = this.defaultAuthorizationServerMetadataCustomizer;
|
||||
}
|
||||
if (this.authorizationServerMetadataCustomizer != null) {
|
||||
authorizationServerMetadataCustomizer =
|
||||
authorizationServerMetadataCustomizer == null ?
|
||||
this.authorizationServerMetadataCustomizer :
|
||||
authorizationServerMetadataCustomizer.andThen(this.authorizationServerMetadataCustomizer);
|
||||
authorizationServerMetadataCustomizer = authorizationServerMetadataCustomizer == null
|
||||
? this.authorizationServerMetadataCustomizer
|
||||
: authorizationServerMetadataCustomizer.andThen(this.authorizationServerMetadataCustomizer);
|
||||
}
|
||||
}
|
||||
return authorizationServerMetadataCustomizer;
|
||||
|
||||
@@ -60,12 +60,21 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2ClientAuthenticationFilter
|
||||
*/
|
||||
public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer = (authenticationConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> authenticationConvertersConsumer = (authenticationConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -76,24 +85,28 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client.
|
||||
*
|
||||
* @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest}
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract client
|
||||
* credentials from {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2ClientAuthenticationToken} used for authenticating the client.
|
||||
* @param authenticationConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract client credentials from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientAuthenticationConfigurer authenticationConverter(AuthenticationConverter authenticationConverter) {
|
||||
public OAuth2ClientAuthenticationConfigurer authenticationConverter(
|
||||
AuthenticationConverter authenticationConverter) {
|
||||
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||
this.authenticationConverters.add(authenticationConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param authenticationConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param authenticationConvertersConsumer the {@code Consumer} providing access to
|
||||
* the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -105,9 +118,10 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OAuth2ClientAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OAuth2ClientAuthenticationToken}
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientAuthenticationConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
@@ -117,11 +131,12 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -133,44 +148,43 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication
|
||||
* and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}.
|
||||
*
|
||||
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling a successful client authentication
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling a successful client
|
||||
* authentication and associating the {@link OAuth2ClientAuthenticationToken} to the
|
||||
* {@link SecurityContext}.
|
||||
* @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used
|
||||
* for handling a successful client authentication
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||
public OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(
|
||||
AuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling a failed client authentication
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling a failed client authentication
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling a failed client
|
||||
* authentication and returning the {@link OAuth2Error Error Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling a failed client authentication
|
||||
* @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2ClientAuthenticationConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2ClientAuthenticationConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name()),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getTokenIntrospectionEndpoint(),
|
||||
HttpMethod.POST.name()),
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenIntrospectionEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getTokenRevocationEndpoint(),
|
||||
HttpMethod.POST.name()),
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenRevocationEndpoint(),
|
||||
HttpMethod.POST.name()),
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getDeviceAuthorizationEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getDeviceAuthorizationEndpoint(),
|
||||
HttpMethod.POST.name()));
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
@@ -178,8 +192,8 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -192,15 +206,16 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
authenticationConverters.addAll(0, this.authenticationConverters);
|
||||
}
|
||||
this.authenticationConvertersConsumer.accept(authenticationConverters);
|
||||
clientAuthenticationFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
clientAuthenticationFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.authenticationSuccessHandler != null) {
|
||||
clientAuthenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler);
|
||||
}
|
||||
if (this.errorResponseHandler != null) {
|
||||
clientAuthenticationFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
|
||||
}
|
||||
httpSecurity.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
httpSecurity.addFilterAfter(postProcess(clientAuthenticationFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,23 +237,24 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity);
|
||||
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils
|
||||
.getRegisteredClientRepository(httpSecurity);
|
||||
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);
|
||||
|
||||
JwtClientAssertionAuthenticationProvider jwtClientAssertionAuthenticationProvider =
|
||||
new JwtClientAssertionAuthenticationProvider(registeredClientRepository, authorizationService);
|
||||
JwtClientAssertionAuthenticationProvider jwtClientAssertionAuthenticationProvider = new JwtClientAssertionAuthenticationProvider(
|
||||
registeredClientRepository, authorizationService);
|
||||
authenticationProviders.add(jwtClientAssertionAuthenticationProvider);
|
||||
|
||||
ClientSecretAuthenticationProvider clientSecretAuthenticationProvider =
|
||||
new ClientSecretAuthenticationProvider(registeredClientRepository, authorizationService);
|
||||
ClientSecretAuthenticationProvider clientSecretAuthenticationProvider = new ClientSecretAuthenticationProvider(
|
||||
registeredClientRepository, authorizationService);
|
||||
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);
|
||||
if (passwordEncoder != null) {
|
||||
clientSecretAuthenticationProvider.setPasswordEncoder(passwordEncoder);
|
||||
}
|
||||
authenticationProviders.add(clientSecretAuthenticationProvider);
|
||||
|
||||
PublicClientAuthenticationProvider publicClientAuthenticationProvider =
|
||||
new PublicClientAuthenticationProvider(registeredClientRepository, authorizationService);
|
||||
PublicClientAuthenticationProvider publicClientAuthenticationProvider = new PublicClientAuthenticationProvider(
|
||||
registeredClientRepository, authorizationService);
|
||||
authenticationProviders.add(publicClientAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -57,7 +57,8 @@ final class OAuth2ConfigurerUtils {
|
||||
}
|
||||
|
||||
static RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) {
|
||||
RegisteredClientRepository registeredClientRepository = httpSecurity.getSharedObject(RegisteredClientRepository.class);
|
||||
RegisteredClientRepository registeredClientRepository = httpSecurity
|
||||
.getSharedObject(RegisteredClientRepository.class);
|
||||
if (registeredClientRepository == null) {
|
||||
registeredClientRepository = getBean(httpSecurity, RegisteredClientRepository.class);
|
||||
httpSecurity.setSharedObject(RegisteredClientRepository.class, registeredClientRepository);
|
||||
@@ -66,7 +67,8 @@ final class OAuth2ConfigurerUtils {
|
||||
}
|
||||
|
||||
static OAuth2AuthorizationService getAuthorizationService(HttpSecurity httpSecurity) {
|
||||
OAuth2AuthorizationService authorizationService = httpSecurity.getSharedObject(OAuth2AuthorizationService.class);
|
||||
OAuth2AuthorizationService authorizationService = httpSecurity
|
||||
.getSharedObject(OAuth2AuthorizationService.class);
|
||||
if (authorizationService == null) {
|
||||
authorizationService = getOptionalBean(httpSecurity, OAuth2AuthorizationService.class);
|
||||
if (authorizationService == null) {
|
||||
@@ -78,7 +80,8 @@ final class OAuth2ConfigurerUtils {
|
||||
}
|
||||
|
||||
static OAuth2AuthorizationConsentService getAuthorizationConsentService(HttpSecurity httpSecurity) {
|
||||
OAuth2AuthorizationConsentService authorizationConsentService = httpSecurity.getSharedObject(OAuth2AuthorizationConsentService.class);
|
||||
OAuth2AuthorizationConsentService authorizationConsentService = httpSecurity
|
||||
.getSharedObject(OAuth2AuthorizationConsentService.class);
|
||||
if (authorizationConsentService == null) {
|
||||
authorizationConsentService = getOptionalBean(httpSecurity, OAuth2AuthorizationConsentService.class);
|
||||
if (authorizationConsentService == null) {
|
||||
@@ -91,23 +94,25 @@ final class OAuth2ConfigurerUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static OAuth2TokenGenerator<? extends OAuth2Token> getTokenGenerator(HttpSecurity httpSecurity) {
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = httpSecurity.getSharedObject(OAuth2TokenGenerator.class);
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = httpSecurity
|
||||
.getSharedObject(OAuth2TokenGenerator.class);
|
||||
if (tokenGenerator == null) {
|
||||
tokenGenerator = getOptionalBean(httpSecurity, OAuth2TokenGenerator.class);
|
||||
if (tokenGenerator == null) {
|
||||
JwtGenerator jwtGenerator = getJwtGenerator(httpSecurity);
|
||||
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
|
||||
OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getAccessTokenCustomizer(httpSecurity);
|
||||
OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer = getAccessTokenCustomizer(
|
||||
httpSecurity);
|
||||
if (accessTokenCustomizer != null) {
|
||||
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer);
|
||||
}
|
||||
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
|
||||
if (jwtGenerator != null) {
|
||||
tokenGenerator = new DelegatingOAuth2TokenGenerator(
|
||||
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
|
||||
} else {
|
||||
tokenGenerator = new DelegatingOAuth2TokenGenerator(
|
||||
accessTokenGenerator, refreshTokenGenerator);
|
||||
tokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator,
|
||||
refreshTokenGenerator);
|
||||
}
|
||||
else {
|
||||
tokenGenerator = new DelegatingOAuth2TokenGenerator(accessTokenGenerator, refreshTokenGenerator);
|
||||
}
|
||||
}
|
||||
httpSecurity.setSharedObject(OAuth2TokenGenerator.class, tokenGenerator);
|
||||
@@ -162,17 +167,20 @@ final class OAuth2ConfigurerUtils {
|
||||
}
|
||||
|
||||
private static OAuth2TokenCustomizer<JwtEncodingContext> getJwtCustomizer(HttpSecurity httpSecurity) {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class);
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,
|
||||
JwtEncodingContext.class);
|
||||
return getOptionalBean(httpSecurity, type);
|
||||
}
|
||||
|
||||
private static OAuth2TokenCustomizer<OAuth2TokenClaimsContext> getAccessTokenCustomizer(HttpSecurity httpSecurity) {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, OAuth2TokenClaimsContext.class);
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class,
|
||||
OAuth2TokenClaimsContext.class);
|
||||
return getOptionalBean(httpSecurity, type);
|
||||
}
|
||||
|
||||
static AuthorizationServerSettings getAuthorizationServerSettings(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = httpSecurity.getSharedObject(AuthorizationServerSettings.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = httpSecurity
|
||||
.getSharedObject(AuthorizationServerSettings.class);
|
||||
if (authorizationServerSettings == null) {
|
||||
authorizationServerSettings = getBean(httpSecurity, AuthorizationServerSettings.class);
|
||||
httpSecurity.setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings);
|
||||
@@ -198,12 +206,12 @@ final class OAuth2ConfigurerUtils {
|
||||
}
|
||||
|
||||
static <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {
|
||||
Map<String, T> beansMap = BeanFactoryUtils.beansOfTypeIncludingAncestors(
|
||||
httpSecurity.getSharedObject(ApplicationContext.class), type);
|
||||
Map<String, T> beansMap = BeanFactoryUtils
|
||||
.beansOfTypeIncludingAncestors(httpSecurity.getSharedObject(ApplicationContext.class), type);
|
||||
if (beansMap.size() > 1) {
|
||||
throw new NoUniqueBeanDefinitionException(type, beansMap.size(),
|
||||
"Expected single matching bean of type '" + type.getName() + "' but found " +
|
||||
beansMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
|
||||
"Expected single matching bean of type '" + type.getName() + "' but found " + beansMap.size() + ": "
|
||||
+ StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
|
||||
}
|
||||
return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null);
|
||||
}
|
||||
|
||||
@@ -56,12 +56,22 @@ import org.springframework.util.StringUtils;
|
||||
public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> deviceAuthorizationRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> deviceAuthorizationRequestConvertersConsumer = (deviceAuthorizationRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> deviceAuthorizationRequestConvertersConsumer = (
|
||||
deviceAuthorizationRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler deviceAuthorizationResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
private String verificationUri;
|
||||
|
||||
/**
|
||||
@@ -72,52 +82,67 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param deviceAuthorizationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationConverter} used when attempting to extract a Device
|
||||
* Authorization Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating
|
||||
* the request.
|
||||
* @param deviceAuthorizationRequestConverter the {@link AuthenticationConverter} used
|
||||
* when attempting to extract a Device Authorization Request from
|
||||
* {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter(AuthenticationConverter deviceAuthorizationRequestConverter) {
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter(
|
||||
AuthenticationConverter deviceAuthorizationRequestConverter) {
|
||||
Assert.notNull(deviceAuthorizationRequestConverter, "deviceAuthorizationRequestConverter cannot be null");
|
||||
this.deviceAuthorizationRequestConverters.add(deviceAuthorizationRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #deviceAuthorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param deviceAuthorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added
|
||||
* {@link #deviceAuthorizationRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param deviceAuthorizationRequestConvertersConsumer the {@code Consumer} providing
|
||||
* access to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverters(
|
||||
Consumer<List<AuthenticationConverter>> deviceAuthorizationRequestConvertersConsumer) {
|
||||
Assert.notNull(deviceAuthorizationRequestConvertersConsumer, "deviceAuthorizationRequestConvertersConsumer cannot be null");
|
||||
Assert.notNull(deviceAuthorizationRequestConvertersConsumer,
|
||||
"deviceAuthorizationRequestConvertersConsumer cannot be null");
|
||||
this.deviceAuthorizationRequestConvertersConsumer = deviceAuthorizationRequestConvertersConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
Assert.notNull(authenticationProvider, "authenticationProvider cannot be null");
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProviders(
|
||||
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
|
||||
@@ -127,34 +152,41 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
|
||||
* and returning the {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.
|
||||
*
|
||||
* @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} and returning the
|
||||
* {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}.
|
||||
* @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler}
|
||||
* used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationResponseHandler(AuthenticationSuccessHandler deviceAuthorizationResponseHandler) {
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationResponseHandler(
|
||||
AuthenticationSuccessHandler deviceAuthorizationResponseHandler) {
|
||||
this.deviceAuthorizationResponseHandler = deviceAuthorizationResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the end-user verification {@code URI} on the authorization server.
|
||||
*
|
||||
* @param verificationUri the end-user verification {@code URI} on the authorization server
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration
|
||||
* @param verificationUri the end-user verification {@code URI} on the authorization
|
||||
* server
|
||||
* @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceAuthorizationEndpointConfigurer verificationUri(String verificationUri) {
|
||||
this.verificationUri = verificationUri;
|
||||
@@ -163,36 +195,36 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
|
||||
|
||||
@Override
|
||||
public void init(HttpSecurity builder) {
|
||||
AuthorizationServerSettings authorizationServerSettings =
|
||||
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getDeviceAuthorizationEndpoint(), HttpMethod.POST.name());
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(builder);
|
||||
this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getDeviceAuthorizationEndpoint(),
|
||||
HttpMethod.POST.name());
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
builder.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders
|
||||
.forEach(authenticationProvider -> builder.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity builder) {
|
||||
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(builder);
|
||||
|
||||
OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter =
|
||||
new OAuth2DeviceAuthorizationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint());
|
||||
OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter = new OAuth2DeviceAuthorizationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint());
|
||||
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.deviceAuthorizationRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.deviceAuthorizationRequestConverters);
|
||||
}
|
||||
this.deviceAuthorizationRequestConvertersConsumer.accept(authenticationConverters);
|
||||
deviceAuthorizationEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
deviceAuthorizationEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.deviceAuthorizationResponseHandler != null) {
|
||||
deviceAuthorizationEndpointFilter.setAuthenticationSuccessHandler(this.deviceAuthorizationResponseHandler);
|
||||
}
|
||||
@@ -222,8 +254,8 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO
|
||||
|
||||
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder);
|
||||
|
||||
OAuth2DeviceAuthorizationRequestAuthenticationProvider deviceAuthorizationRequestAuthenticationProvider =
|
||||
new OAuth2DeviceAuthorizationRequestAuthenticationProvider(authorizationService);
|
||||
OAuth2DeviceAuthorizationRequestAuthenticationProvider deviceAuthorizationRequestAuthenticationProvider = new OAuth2DeviceAuthorizationRequestAuthenticationProvider(
|
||||
authorizationService);
|
||||
authenticationProviders.add(deviceAuthorizationRequestAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -61,12 +61,22 @@ import org.springframework.util.StringUtils;
|
||||
public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> deviceVerificationRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> deviceVerificationRequestConvertersConsumer = (deviceVerificationRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> deviceVerificationRequestConvertersConsumer = (
|
||||
deviceVerificationRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler deviceVerificationResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
private String consentPage;
|
||||
|
||||
/**
|
||||
@@ -77,52 +87,71 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationConverter} used when attempting to extract a Device
|
||||
* Verification Request (or Device Authorization Consent) from
|
||||
* {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2DeviceVerificationAuthenticationToken} or
|
||||
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating
|
||||
* the request.
|
||||
* @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used
|
||||
* when attempting to extract a Device Verification Request (or Device Authorization
|
||||
* Consent) from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter(AuthenticationConverter deviceVerificationRequestConverter) {
|
||||
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter(
|
||||
AuthenticationConverter deviceVerificationRequestConverter) {
|
||||
Assert.notNull(deviceVerificationRequestConverter, "deviceVerificationRequestConverter cannot be null");
|
||||
this.deviceVerificationRequestConverters.add(deviceVerificationRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #deviceVerificationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param deviceVerificationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added
|
||||
* {@link #deviceVerificationRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param deviceVerificationRequestConvertersConsumer the {@code Consumer} providing
|
||||
* access to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverters(
|
||||
Consumer<List<AuthenticationConverter>> deviceVerificationRequestConvertersConsumer) {
|
||||
Assert.notNull(deviceVerificationRequestConvertersConsumer, "deviceVerificationRequestConvertersConsumer cannot be null");
|
||||
Assert.notNull(deviceVerificationRequestConvertersConsumer,
|
||||
"deviceVerificationRequestConvertersConsumer cannot be null");
|
||||
this.deviceVerificationRequestConvertersConsumer = deviceVerificationRequestConvertersConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OAuth2DeviceVerificationAuthenticationToken} or
|
||||
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or
|
||||
* {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
public OAuth2DeviceVerificationEndpointConfigurer authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
Assert.notNull(authenticationProvider, "authenticationProvider cannot be null");
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer authenticationProviders(
|
||||
Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer) {
|
||||
@@ -132,59 +161,67 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
|
||||
* and returning the response.
|
||||
*
|
||||
* @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2DeviceVerificationAuthenticationToken} and returning the response.
|
||||
* @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler}
|
||||
* used for handling an {@link OAuth2DeviceVerificationAuthenticationToken}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler(AuthenticationSuccessHandler deviceVerificationResponseHandler) {
|
||||
public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler(
|
||||
AuthenticationSuccessHandler deviceVerificationResponseHandler) {
|
||||
this.deviceVerificationResponseHandler = deviceVerificationResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2DeviceVerificationEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the URI to redirect Resource Owners to if consent is required during
|
||||
* the {@code device_code} flow. A default consent page will be generated when
|
||||
* this attribute is not specified.
|
||||
* Specify the URI to redirect Resource Owners to if consent is required during the
|
||||
* {@code device_code} flow. A default consent page will be generated when this
|
||||
* attribute is not specified.
|
||||
*
|
||||
* If a URI is specified, applications are required to process the specified URI to generate
|
||||
* a consent page. The query string will contain the following parameters:
|
||||
* If a URI is specified, applications are required to process the specified URI to
|
||||
* generate a consent page. The query string will contain the following parameters:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@code client_id} - the client identifier</li>
|
||||
* <li>{@code scope} - a space-delimited list of scopes present in the device authorization request</li>
|
||||
* <li>{@code scope} - a space-delimited list of scopes present in the device
|
||||
* authorization request</li>
|
||||
* <li>{@code state} - a CSRF protection token</li>
|
||||
* <li>{@code user_code} - the user code</li>
|
||||
* </ul>
|
||||
*
|
||||
* In general, the consent page should create a form that submits
|
||||
* a request with the following requirements:
|
||||
* In general, the consent page should create a form that submits a request with the
|
||||
* following requirements:
|
||||
*
|
||||
* <ul>
|
||||
* <li>It must be an HTTP POST</li>
|
||||
* <li>It must be submitted to {@link AuthorizationServerSettings#getDeviceVerificationEndpoint()}</li>
|
||||
* <li>It must be submitted to
|
||||
* {@link AuthorizationServerSettings#getDeviceVerificationEndpoint()}</li>
|
||||
* <li>It must include the received {@code client_id} as an HTTP parameter</li>
|
||||
* <li>It must include the received {@code state} as an HTTP parameter</li>
|
||||
* <li>It must include the list of {@code scope}s the {@code Resource Owner}
|
||||
* consented to as an HTTP parameter</li>
|
||||
* <li>It must include the list of {@code scope}s the {@code Resource Owner} consented
|
||||
* to as an HTTP parameter</li>
|
||||
* <li>It must include the received {@code user_code} as an HTTP parameter</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent")
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration
|
||||
* @param consentPage the URI of the custom consent page to redirect to if consent is
|
||||
* required (e.g. "/oauth2/consent")
|
||||
* @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2DeviceVerificationEndpointConfigurer consentPage(String consentPage) {
|
||||
this.consentPage = consentPage;
|
||||
@@ -193,14 +230,12 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
|
||||
@Override
|
||||
public void init(HttpSecurity builder) {
|
||||
AuthorizationServerSettings authorizationServerSettings =
|
||||
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(builder);
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getDeviceVerificationEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getDeviceVerificationEndpoint(),
|
||||
HttpMethod.GET.name()),
|
||||
new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getDeviceVerificationEndpoint(),
|
||||
new AntPathRequestMatcher(authorizationServerSettings.getDeviceVerificationEndpoint(),
|
||||
HttpMethod.POST.name()));
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(builder);
|
||||
@@ -208,27 +243,25 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
builder.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders
|
||||
.forEach(authenticationProvider -> builder.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(HttpSecurity builder) {
|
||||
AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings =
|
||||
OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(builder);
|
||||
|
||||
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter =
|
||||
new OAuth2DeviceVerificationEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getDeviceVerificationEndpoint());
|
||||
OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter = new OAuth2DeviceVerificationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getDeviceVerificationEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.deviceVerificationRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.deviceVerificationRequestConverters);
|
||||
}
|
||||
this.deviceVerificationRequestConvertersConsumer.accept(authenticationConverters);
|
||||
deviceVerificationEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
deviceVerificationEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.deviceVerificationResponseHandler != null) {
|
||||
deviceVerificationEndpointFilter.setAuthenticationSuccessHandler(this.deviceVerificationResponseHandler);
|
||||
}
|
||||
@@ -238,7 +271,8 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
if (StringUtils.hasText(this.consentPage)) {
|
||||
deviceVerificationEndpointFilter.setConsentPage(this.consentPage);
|
||||
}
|
||||
builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -256,12 +290,11 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity builder) {
|
||||
RegisteredClientRepository registeredClientRepository =
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(builder);
|
||||
OAuth2AuthorizationService authorizationService =
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(builder);
|
||||
OAuth2AuthorizationConsentService authorizationConsentService =
|
||||
OAuth2ConfigurerUtils.getAuthorizationConsentService(builder);
|
||||
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils
|
||||
.getRegisteredClientRepository(builder);
|
||||
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder);
|
||||
OAuth2AuthorizationConsentService authorizationConsentService = OAuth2ConfigurerUtils
|
||||
.getAuthorizationConsentService(builder);
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -63,12 +63,22 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2TokenEndpointFilter
|
||||
*/
|
||||
public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> accessTokenRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer = (accessTokenRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> accessTokenRequestConvertersConsumer = (
|
||||
accessTokenRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler accessTokenResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -79,24 +89,29 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the authorization grant.
|
||||
*
|
||||
* @param accessTokenRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest}
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an Access
|
||||
* Token Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the
|
||||
* authorization grant.
|
||||
* @param accessTokenRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract an Access Token Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2TokenEndpointConfigurer accessTokenRequestConverter(AuthenticationConverter accessTokenRequestConverter) {
|
||||
public OAuth2TokenEndpointConfigurer accessTokenRequestConverter(
|
||||
AuthenticationConverter accessTokenRequestConverter) {
|
||||
Assert.notNull(accessTokenRequestConverter, "accessTokenRequestConverter cannot be null");
|
||||
this.accessTokenRequestConverters.add(accessTokenRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access
|
||||
* to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -108,9 +123,10 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of
|
||||
* {@link OAuth2AuthorizationGrantAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2TokenEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
@@ -120,11 +136,12 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -136,22 +153,25 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken}
|
||||
* and returning the {@link OAuth2AccessTokenResponse Access Token Response}.
|
||||
*
|
||||
* @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken}
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2AccessTokenAuthenticationToken} and returning the
|
||||
* {@link OAuth2AccessTokenResponse Access Token Response}.
|
||||
* @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for
|
||||
* handling an {@link OAuth2AccessTokenAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2TokenEndpointConfigurer accessTokenResponseHandler(AuthenticationSuccessHandler accessTokenResponseHandler) {
|
||||
public OAuth2TokenEndpointConfigurer accessTokenResponseHandler(
|
||||
AuthenticationSuccessHandler accessTokenResponseHandler) {
|
||||
this.accessTokenResponseHandler = accessTokenResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2TokenEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OAuth2TokenEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
@@ -161,35 +181,34 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name());
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenEndpoint(),
|
||||
HttpMethod.POST.name());
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OAuth2TokenEndpointFilter tokenEndpointFilter =
|
||||
new OAuth2TokenEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getTokenEndpoint());
|
||||
OAuth2TokenEndpointFilter tokenEndpointFilter = new OAuth2TokenEndpointFilter(authenticationManager,
|
||||
authorizationServerSettings.getTokenEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.accessTokenRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.accessTokenRequestConverters);
|
||||
}
|
||||
this.accessTokenRequestConvertersConsumer.accept(authenticationConverters);
|
||||
tokenEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
tokenEndpointFilter.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.accessTokenResponseHandler != null) {
|
||||
tokenEndpointFilter.setAuthenticationSuccessHandler(this.accessTokenResponseHandler);
|
||||
}
|
||||
@@ -219,26 +238,27 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity);
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = OAuth2ConfigurerUtils
|
||||
.getTokenGenerator(httpSecurity);
|
||||
|
||||
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider =
|
||||
new OAuth2AuthorizationCodeAuthenticationProvider(authorizationService, tokenGenerator);
|
||||
OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(
|
||||
authorizationService, tokenGenerator);
|
||||
SessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class);
|
||||
if (sessionRegistry != null) {
|
||||
authorizationCodeAuthenticationProvider.setSessionRegistry(sessionRegistry);
|
||||
}
|
||||
authenticationProviders.add(authorizationCodeAuthenticationProvider);
|
||||
|
||||
OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider =
|
||||
new OAuth2RefreshTokenAuthenticationProvider(authorizationService, tokenGenerator);
|
||||
OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(
|
||||
authorizationService, tokenGenerator);
|
||||
authenticationProviders.add(refreshTokenAuthenticationProvider);
|
||||
|
||||
OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider =
|
||||
new OAuth2ClientCredentialsAuthenticationProvider(authorizationService, tokenGenerator);
|
||||
OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(
|
||||
authorizationService, tokenGenerator);
|
||||
authenticationProviders.add(clientCredentialsAuthenticationProvider);
|
||||
|
||||
OAuth2DeviceCodeAuthenticationProvider deviceCodeAuthenticationProvider =
|
||||
new OAuth2DeviceCodeAuthenticationProvider(authorizationService, tokenGenerator);
|
||||
OAuth2DeviceCodeAuthenticationProvider deviceCodeAuthenticationProvider = new OAuth2DeviceCodeAuthenticationProvider(
|
||||
authorizationService, tokenGenerator);
|
||||
authenticationProviders.add(deviceCodeAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -53,12 +53,22 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2TokenIntrospectionEndpointFilter
|
||||
*/
|
||||
public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> introspectionRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer = (introspectionRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> introspectionRequestConvertersConsumer = (
|
||||
introspectionRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler introspectionResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -69,25 +79,32 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param introspectionRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an
|
||||
* Introspection Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the
|
||||
* request.
|
||||
* @param introspectionRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract an Introspection Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(AuthenticationConverter introspectionRequestConverter) {
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(
|
||||
AuthenticationConverter introspectionRequestConverter) {
|
||||
Assert.notNull(introspectionRequestConverter, "introspectionRequestConverter cannot be null");
|
||||
this.introspectionRequestConverters.add(introspectionRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param introspectionRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param introspectionRequestConvertersConsumer the {@code Consumer} providing access
|
||||
* to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverters(
|
||||
@@ -98,24 +115,29 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of
|
||||
* {@link OAuth2TokenIntrospectionAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
Assert.notNull(authenticationProvider, "authenticationProvider cannot be null");
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer authenticationProviders(
|
||||
@@ -126,58 +148,65 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}.
|
||||
*
|
||||
* @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2TokenIntrospectionAuthenticationToken}.
|
||||
* @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used
|
||||
* for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler(AuthenticationSuccessHandler introspectionResponseHandler) {
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler(
|
||||
AuthenticationSuccessHandler introspectionResponseHandler) {
|
||||
this.introspectionResponseHandler = introspectionResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name());
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenIntrospectionEndpoint(),
|
||||
HttpMethod.POST.name());
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter =
|
||||
new OAuth2TokenIntrospectionEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint());
|
||||
OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter = new OAuth2TokenIntrospectionEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.introspectionRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.introspectionRequestConverters);
|
||||
}
|
||||
this.introspectionRequestConvertersConsumer.accept(authenticationConverters);
|
||||
introspectionEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
introspectionEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.introspectionResponseHandler != null) {
|
||||
introspectionEndpointFilter.setAuthenticationSuccessHandler(this.introspectionResponseHandler);
|
||||
}
|
||||
@@ -203,10 +232,9 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider =
|
||||
new OAuth2TokenIntrospectionAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider = new OAuth2TokenIntrospectionAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
authenticationProviders.add(tokenIntrospectionAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -52,12 +52,22 @@ import org.springframework.util.Assert;
|
||||
* @see OAuth2TokenRevocationEndpointFilter
|
||||
*/
|
||||
public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> revocationRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer = (revocationRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> revocationRequestConvertersConsumer = (
|
||||
revocationRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler revocationResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -68,25 +78,32 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param revocationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke
|
||||
* Token Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the
|
||||
* request.
|
||||
* @param revocationRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract a Revoke Token Request from {@link HttpServletRequest}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(AuthenticationConverter revocationRequestConverter) {
|
||||
public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(
|
||||
AuthenticationConverter revocationRequestConverter) {
|
||||
Assert.notNull(revocationRequestConverter, "revocationRequestConverter cannot be null");
|
||||
this.revocationRequestConverters.add(revocationRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #revocationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param revocationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #revocationRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param revocationRequestConvertersConsumer the {@code Consumer} providing access to
|
||||
* the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverters(
|
||||
@@ -97,24 +114,29 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating a type of
|
||||
* {@link OAuth2TokenRevocationAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
public OAuth2TokenRevocationEndpointConfigurer authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
Assert.notNull(authenticationProvider, "authenticationProvider cannot be null");
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer authenticationProviders(
|
||||
@@ -125,58 +147,65 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}.
|
||||
*
|
||||
* @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OAuth2TokenRevocationAuthenticationToken}.
|
||||
* @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for
|
||||
* handling an {@link OAuth2TokenRevocationAuthenticationToken}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer revocationResponseHandler(AuthenticationSuccessHandler revocationResponseHandler) {
|
||||
public OAuth2TokenRevocationEndpointConfigurer revocationResponseHandler(
|
||||
AuthenticationSuccessHandler revocationResponseHandler) {
|
||||
this.revocationResponseHandler = revocationResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OAuth2TokenRevocationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OAuth2TokenRevocationEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
authorizationServerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name());
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenRevocationEndpoint(),
|
||||
HttpMethod.POST.name());
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter =
|
||||
new OAuth2TokenRevocationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint());
|
||||
OAuth2TokenRevocationEndpointFilter revocationEndpointFilter = new OAuth2TokenRevocationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.revocationRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.revocationRequestConverters);
|
||||
}
|
||||
this.revocationRequestConvertersConsumer.accept(authenticationConverters);
|
||||
revocationEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
revocationEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.revocationResponseHandler != null) {
|
||||
revocationEndpointFilter.setAuthenticationSuccessHandler(this.revocationResponseHandler);
|
||||
}
|
||||
@@ -202,8 +231,8 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider =
|
||||
new OAuth2TokenRevocationAuthenticationProvider(OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider = new OAuth2TokenRevocationAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
authenticationProviders.add(tokenRevocationAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -56,12 +56,22 @@ import org.springframework.util.Assert;
|
||||
* @see OidcClientRegistrationEndpointFilter
|
||||
*/
|
||||
public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> clientRegistrationRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (clientRegistrationRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer = (
|
||||
clientRegistrationRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler clientRegistrationResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -72,11 +82,15 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Client
|
||||
* Registration Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OidcClientRegistrationAuthenticationToken} used for authenticating the
|
||||
* request.
|
||||
* @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used
|
||||
* when attempting to extract a Client Registration Request from
|
||||
* {@link HttpServletRequest}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverter(
|
||||
@@ -87,41 +101,50 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #clientRegistrationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added
|
||||
* {@link #clientRegistrationRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing
|
||||
* access to the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters(
|
||||
Consumer<List<AuthenticationConverter>> clientRegistrationRequestConvertersConsumer) {
|
||||
Assert.notNull(clientRegistrationRequestConvertersConsumer, "clientRegistrationRequestConvertersConsumer cannot be null");
|
||||
Assert.notNull(clientRegistrationRequestConvertersConsumer,
|
||||
"clientRegistrationRequestConvertersConsumer cannot be null");
|
||||
this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OidcClientRegistrationAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OidcClientRegistrationAuthenticationToken}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
public OidcClientRegistrationEndpointConfigurer authenticationProvider(
|
||||
AuthenticationProvider authenticationProvider) {
|
||||
Assert.notNull(authenticationProvider, "authenticationProvider cannot be null");
|
||||
this.authenticationProviders.add(authenticationProvider);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer authenticationProviders(
|
||||
@@ -132,68 +155,73 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken}
|
||||
* and returning the {@link OidcClientRegistration Client Registration Response}.
|
||||
*
|
||||
* @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OidcClientRegistrationAuthenticationToken} and returning the
|
||||
* {@link OidcClientRegistration Client Registration Response}.
|
||||
* @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler}
|
||||
* used for handling an {@link OidcClientRegistrationAuthenticationToken}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(AuthenticationSuccessHandler clientRegistrationResponseHandler) {
|
||||
public OidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(
|
||||
AuthenticationSuccessHandler clientRegistrationResponseHandler) {
|
||||
this.clientRegistrationResponseHandler = clientRegistrationResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OidcClientRegistrationEndpointConfigurer} for further
|
||||
* configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcClientRegistrationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
public OidcClientRegistrationEndpointConfigurer errorResponseHandler(
|
||||
AuthenticationFailureHandler errorResponseHandler) {
|
||||
this.errorResponseHandler = errorResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
String clientRegistrationEndpointUri = authorizationServerSettings.getOidcClientRegistrationEndpoint();
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()),
|
||||
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name())
|
||||
);
|
||||
new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name()));
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter =
|
||||
new OidcClientRegistrationEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getOidcClientRegistrationEndpoint());
|
||||
OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = new OidcClientRegistrationEndpointFilter(
|
||||
authenticationManager, authorizationServerSettings.getOidcClientRegistrationEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.clientRegistrationRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.clientRegistrationRequestConverters);
|
||||
}
|
||||
this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters);
|
||||
oidcClientRegistrationEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
oidcClientRegistrationEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.clientRegistrationResponseHandler != null) {
|
||||
oidcClientRegistrationEndpointFilter
|
||||
.setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler);
|
||||
.setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler);
|
||||
}
|
||||
if (this.errorResponseHandler != null) {
|
||||
oidcClientRegistrationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
|
||||
@@ -217,21 +245,19 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider =
|
||||
new OidcClientRegistrationAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity));
|
||||
OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider = new OidcClientRegistrationAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity));
|
||||
PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class);
|
||||
if (passwordEncoder != null) {
|
||||
oidcClientRegistrationAuthenticationProvider.setPasswordEncoder(passwordEncoder);
|
||||
}
|
||||
authenticationProviders.add(oidcClientRegistrationAuthenticationProvider);
|
||||
|
||||
OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider =
|
||||
new OidcClientConfigurationAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider = new OidcClientConfigurationAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
authenticationProviders.add(oidcClientConfigurationAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -42,7 +42,9 @@ import org.springframework.web.util.UriComponentsBuilder;
|
||||
* @see OidcUserInfoEndpointConfigurer
|
||||
*/
|
||||
public final class OidcConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private final Map<Class<? extends AbstractOAuth2Configurer>, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>();
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
/**
|
||||
@@ -50,27 +52,30 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
|
||||
*/
|
||||
OidcConfigurer(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
addConfigurer(OidcProviderConfigurationEndpointConfigurer.class, new OidcProviderConfigurationEndpointConfigurer(objectPostProcessor));
|
||||
addConfigurer(OidcProviderConfigurationEndpointConfigurer.class,
|
||||
new OidcProviderConfigurationEndpointConfigurer(objectPostProcessor));
|
||||
addConfigurer(OidcLogoutEndpointConfigurer.class, new OidcLogoutEndpointConfigurer(objectPostProcessor));
|
||||
addConfigurer(OidcUserInfoEndpointConfigurer.class, new OidcUserInfoEndpointConfigurer(objectPostProcessor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OpenID Connect 1.0 Provider Configuration Endpoint.
|
||||
*
|
||||
* @param providerConfigurationEndpointCustomizer the {@link Customizer} providing access to the {@link OidcProviderConfigurationEndpointConfigurer}
|
||||
* @param providerConfigurationEndpointCustomizer the {@link Customizer} providing
|
||||
* access to the {@link OidcProviderConfigurationEndpointConfigurer}
|
||||
* @return the {@link OidcConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcConfigurer providerConfigurationEndpoint(Customizer<OidcProviderConfigurationEndpointConfigurer> providerConfigurationEndpointCustomizer) {
|
||||
providerConfigurationEndpointCustomizer.customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class));
|
||||
public OidcConfigurer providerConfigurationEndpoint(
|
||||
Customizer<OidcProviderConfigurationEndpointConfigurer> providerConfigurationEndpointCustomizer) {
|
||||
providerConfigurationEndpointCustomizer
|
||||
.customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the OpenID Connect 1.0 RP-Initiated Logout Endpoint.
|
||||
*
|
||||
* @param logoutEndpointCustomizer the {@link Customizer} providing access to the {@link OidcLogoutEndpointConfigurer}
|
||||
* @param logoutEndpointCustomizer the {@link Customizer} providing access to the
|
||||
* {@link OidcLogoutEndpointConfigurer}
|
||||
* @return the {@link OidcConfigurer} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -81,13 +86,14 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
/**
|
||||
* Configures the OpenID Connect Dynamic Client Registration 1.0 Endpoint.
|
||||
*
|
||||
* @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access to the {@link OidcClientRegistrationEndpointConfigurer}
|
||||
* @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access
|
||||
* to the {@link OidcClientRegistrationEndpointConfigurer}
|
||||
* @return the {@link OidcConfigurer} for further configuration
|
||||
*/
|
||||
public OidcConfigurer clientRegistrationEndpoint(Customizer<OidcClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer) {
|
||||
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer =
|
||||
getConfigurer(OidcClientRegistrationEndpointConfigurer.class);
|
||||
public OidcConfigurer clientRegistrationEndpoint(
|
||||
Customizer<OidcClientRegistrationEndpointConfigurer> clientRegistrationEndpointCustomizer) {
|
||||
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(
|
||||
OidcClientRegistrationEndpointConfigurer.class);
|
||||
if (clientRegistrationEndpointConfigurer == null) {
|
||||
addConfigurer(OidcClientRegistrationEndpointConfigurer.class,
|
||||
new OidcClientRegistrationEndpointConfigurer(getObjectPostProcessor()));
|
||||
@@ -99,8 +105,8 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
/**
|
||||
* Configures the OpenID Connect 1.0 UserInfo Endpoint.
|
||||
*
|
||||
* @param userInfoEndpointCustomizer the {@link Customizer} providing access to the {@link OidcUserInfoEndpointConfigurer}
|
||||
* @param userInfoEndpointCustomizer the {@link Customizer} providing access to the
|
||||
* {@link OidcUserInfoEndpointConfigurer}
|
||||
* @return the {@link OidcConfigurer} for further configuration
|
||||
*/
|
||||
public OidcConfigurer userInfoEndpoint(Customizer<OidcUserInfoEndpointConfigurer> userInfoEndpointCustomizer) {
|
||||
@@ -120,23 +126,25 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer =
|
||||
getConfigurer(OidcClientRegistrationEndpointConfigurer.class);
|
||||
OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer(
|
||||
OidcClientRegistrationEndpointConfigurer.class);
|
||||
if (clientRegistrationEndpointConfigurer != null) {
|
||||
OidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer =
|
||||
getConfigurer(OidcProviderConfigurationEndpointConfigurer.class);
|
||||
OidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer(
|
||||
OidcProviderConfigurationEndpointConfigurer.class);
|
||||
|
||||
providerConfigurationEndpointConfigurer
|
||||
.addDefaultProviderConfigurationCustomizer((builder) -> {
|
||||
AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();
|
||||
String issuer = authorizationServerContext.getIssuer();
|
||||
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings();
|
||||
providerConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> {
|
||||
AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext();
|
||||
String issuer = authorizationServerContext.getIssuer();
|
||||
AuthorizationServerSettings authorizationServerSettings = authorizationServerContext
|
||||
.getAuthorizationServerSettings();
|
||||
|
||||
String clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer)
|
||||
.path(authorizationServerSettings.getOidcClientRegistrationEndpoint()).build().toUriString();
|
||||
String clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer)
|
||||
.path(authorizationServerSettings.getOidcClientRegistrationEndpoint())
|
||||
.build()
|
||||
.toUriString();
|
||||
|
||||
builder.clientRegistrationEndpoint(clientRegistrationEndpoint);
|
||||
});
|
||||
builder.clientRegistrationEndpoint(clientRegistrationEndpoint);
|
||||
});
|
||||
}
|
||||
|
||||
this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity));
|
||||
|
||||
@@ -53,12 +53,21 @@ import org.springframework.util.Assert;
|
||||
* @see OidcLogoutEndpointFilter
|
||||
*/
|
||||
public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> logoutRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> logoutRequestConvertersConsumer = (logoutRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> logoutRequestConvertersConsumer = (logoutRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler logoutResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
/**
|
||||
@@ -69,25 +78,26 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OidcLogoutAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param logoutRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest}
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract a Logout
|
||||
* Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OidcLogoutAuthenticationToken} used for authenticating the request.
|
||||
* @param logoutRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract a Logout Request from {@link HttpServletRequest}
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer logoutRequestConverter(
|
||||
AuthenticationConverter logoutRequestConverter) {
|
||||
public OidcLogoutEndpointConfigurer logoutRequestConverter(AuthenticationConverter logoutRequestConverter) {
|
||||
Assert.notNull(logoutRequestConverter, "logoutRequestConverter cannot be null");
|
||||
this.logoutRequestConverters.add(logoutRequestConverter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #logoutRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param logoutRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #logoutRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param logoutRequestConvertersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer logoutRequestConverters(
|
||||
@@ -98,9 +108,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcLogoutAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcLogoutAuthenticationToken}
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OidcLogoutAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OidcLogoutAuthenticationToken}
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
@@ -110,11 +121,12 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer authenticationProviders(
|
||||
@@ -125,10 +137,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken}
|
||||
* and performing the logout.
|
||||
*
|
||||
* @param logoutResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken}
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OidcLogoutAuthenticationToken} and performing the logout.
|
||||
* @param logoutResponseHandler the {@link AuthenticationSuccessHandler} used for
|
||||
* handling an {@link OidcLogoutAuthenticationToken}
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer logoutResponseHandler(AuthenticationSuccessHandler logoutResponseHandler) {
|
||||
@@ -137,10 +149,11 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OidcLogoutEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcLogoutEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) {
|
||||
@@ -150,38 +163,36 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
String logoutEndpointUri = authorizationServerSettings.getOidcLogoutEndpoint();
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()),
|
||||
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name())
|
||||
);
|
||||
this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()),
|
||||
new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name()));
|
||||
|
||||
List<AuthenticationProvider> authenticationProviders = createDefaultAuthenticationProviders(httpSecurity);
|
||||
if (!this.authenticationProviders.isEmpty()) {
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OidcLogoutEndpointFilter oidcLogoutEndpointFilter =
|
||||
new OidcLogoutEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getOidcLogoutEndpoint());
|
||||
OidcLogoutEndpointFilter oidcLogoutEndpointFilter = new OidcLogoutEndpointFilter(authenticationManager,
|
||||
authorizationServerSettings.getOidcLogoutEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.logoutRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.logoutRequestConverters);
|
||||
}
|
||||
this.logoutRequestConvertersConsumer.accept(authenticationConverters);
|
||||
oidcLogoutEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
oidcLogoutEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.logoutResponseHandler != null) {
|
||||
oidcLogoutEndpointFilter.setAuthenticationSuccessHandler(this.logoutResponseHandler);
|
||||
}
|
||||
@@ -207,11 +218,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer
|
||||
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider =
|
||||
new OidcLogoutAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
httpSecurity.getSharedObject(SessionRegistry.class));
|
||||
OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider = new OidcLogoutAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity),
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity),
|
||||
httpSecurity.getSharedObject(SessionRegistry.class));
|
||||
authenticationProviders.add(oidcLogoutAuthenticationProvider);
|
||||
|
||||
return authenticationProviders;
|
||||
|
||||
@@ -35,8 +35,11 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* @see OidcProviderConfigurationEndpointFilter
|
||||
*/
|
||||
public final class OidcProviderConfigurationEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer;
|
||||
|
||||
private Consumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer;
|
||||
|
||||
/**
|
||||
@@ -47,11 +50,13 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
|
||||
* allowing the ability to customize the claims of the OpenID Provider's configuration.
|
||||
*
|
||||
* @param providerConfigurationCustomizer the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder}
|
||||
* @return the {@link OidcProviderConfigurationEndpointConfigurer} for further configuration
|
||||
* Sets the {@code Consumer} providing access to the
|
||||
* {@link OidcProviderConfiguration.Builder} allowing the ability to customize the
|
||||
* claims of the OpenID Provider's configuration.
|
||||
* @param providerConfigurationCustomizer the {@code Consumer} providing access to the
|
||||
* {@link OidcProviderConfiguration.Builder}
|
||||
* @return the {@link OidcProviderConfigurationEndpointConfigurer} for further
|
||||
* configuration
|
||||
*/
|
||||
public OidcProviderConfigurationEndpointConfigurer providerConfigurationCustomizer(
|
||||
Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer) {
|
||||
@@ -61,27 +66,25 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO
|
||||
|
||||
void addDefaultProviderConfigurationCustomizer(
|
||||
Consumer<OidcProviderConfiguration.Builder> defaultProviderConfigurationCustomizer) {
|
||||
this.defaultProviderConfigurationCustomizer =
|
||||
this.defaultProviderConfigurationCustomizer == null ?
|
||||
defaultProviderConfigurationCustomizer :
|
||||
this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer);
|
||||
this.defaultProviderConfigurationCustomizer = this.defaultProviderConfigurationCustomizer == null
|
||||
? defaultProviderConfigurationCustomizer
|
||||
: this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
this.requestMatcher = new AntPathRequestMatcher(
|
||||
"/.well-known/openid-configuration", HttpMethod.GET.name());
|
||||
this.requestMatcher = new AntPathRequestMatcher("/.well-known/openid-configuration", HttpMethod.GET.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter =
|
||||
new OidcProviderConfigurationEndpointFilter();
|
||||
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = new OidcProviderConfigurationEndpointFilter();
|
||||
Consumer<OidcProviderConfiguration.Builder> providerConfigurationCustomizer = getProviderConfigurationCustomizer();
|
||||
if (providerConfigurationCustomizer != null) {
|
||||
oidcProviderConfigurationEndpointFilter.setProviderConfigurationCustomizer(providerConfigurationCustomizer);
|
||||
}
|
||||
httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
|
||||
httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter),
|
||||
AbstractPreAuthenticatedProcessingFilter.class);
|
||||
}
|
||||
|
||||
private Consumer<OidcProviderConfiguration.Builder> getProviderConfigurationCustomizer() {
|
||||
@@ -91,10 +94,9 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO
|
||||
providerConfigurationCustomizer = this.defaultProviderConfigurationCustomizer;
|
||||
}
|
||||
if (this.providerConfigurationCustomizer != null) {
|
||||
providerConfigurationCustomizer =
|
||||
providerConfigurationCustomizer == null ?
|
||||
this.providerConfigurationCustomizer :
|
||||
providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer);
|
||||
providerConfigurationCustomizer = providerConfigurationCustomizer == null
|
||||
? this.providerConfigurationCustomizer
|
||||
: providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer);
|
||||
}
|
||||
}
|
||||
return providerConfigurationCustomizer;
|
||||
|
||||
@@ -59,13 +59,23 @@ import org.springframework.util.Assert;
|
||||
* @see OidcUserInfoEndpointFilter
|
||||
*/
|
||||
public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer {
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private final List<AuthenticationConverter> userInfoRequestConverters = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationConverter>> userInfoRequestConvertersConsumer = (userInfoRequestConverters) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationConverter>> userInfoRequestConvertersConsumer = (userInfoRequestConverters) -> {
|
||||
};
|
||||
|
||||
private final List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {};
|
||||
|
||||
private Consumer<List<AuthenticationProvider>> authenticationProvidersConsumer = (authenticationProviders) -> {
|
||||
};
|
||||
|
||||
private AuthenticationSuccessHandler userInfoResponseHandler;
|
||||
|
||||
private AuthenticationFailureHandler errorResponseHandler;
|
||||
|
||||
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper;
|
||||
|
||||
/**
|
||||
@@ -76,10 +86,11 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest}
|
||||
* to an instance of {@link OidcUserInfoAuthenticationToken} used for authenticating the request.
|
||||
*
|
||||
* @param userInfoRequestConverter an {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest}
|
||||
* Adds an {@link AuthenticationConverter} used when attempting to extract an UserInfo
|
||||
* Request from {@link HttpServletRequest} to an instance of
|
||||
* {@link OidcUserInfoAuthenticationToken} used for authenticating the request.
|
||||
* @param userInfoRequestConverter an {@link AuthenticationConverter} used when
|
||||
* attempting to extract an UserInfo Request from {@link HttpServletRequest}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -90,11 +101,13 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #userInfoRequestConverter(AuthenticationConverter) AuthenticationConverter}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}.
|
||||
*
|
||||
* @param userInfoRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #userInfoRequestConverter(AuthenticationConverter)
|
||||
* AuthenticationConverter}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationConverter}.
|
||||
* @param userInfoRequestConvertersConsumer the {@code Consumer} providing access to
|
||||
* the {@code List} of default and (optionally) added
|
||||
* {@link AuthenticationConverter}'s
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -106,9 +119,10 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcUserInfoAuthenticationToken}.
|
||||
*
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcUserInfoAuthenticationToken}
|
||||
* Adds an {@link AuthenticationProvider} used for authenticating an
|
||||
* {@link OidcUserInfoAuthenticationToken}.
|
||||
* @param authenticationProvider an {@link AuthenticationProvider} used for
|
||||
* authenticating an {@link OidcUserInfoAuthenticationToken}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -119,11 +133,12 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default
|
||||
* and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s
|
||||
* allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}.
|
||||
*
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* Sets the {@code Consumer} providing access to the {@code List} of default and
|
||||
* (optionally) added {@link #authenticationProvider(AuthenticationProvider)
|
||||
* AuthenticationProvider}'s allowing the ability to add, remove, or customize a
|
||||
* specific {@link AuthenticationProvider}.
|
||||
* @param authenticationProvidersConsumer the {@code Consumer} providing access to the
|
||||
* {@code List} of default and (optionally) added {@link AuthenticationProvider}'s
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -135,23 +150,26 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken}
|
||||
* and returning the {@link OidcUserInfo UserInfo Response}.
|
||||
*
|
||||
* @param userInfoResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken}
|
||||
* Sets the {@link AuthenticationSuccessHandler} used for handling an
|
||||
* {@link OidcUserInfoAuthenticationToken} and returning the {@link OidcUserInfo
|
||||
* UserInfo Response}.
|
||||
* @param userInfoResponseHandler the {@link AuthenticationSuccessHandler} used for
|
||||
* handling an {@link OidcUserInfoAuthenticationToken}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public OidcUserInfoEndpointConfigurer userInfoResponseHandler(AuthenticationSuccessHandler userInfoResponseHandler) {
|
||||
public OidcUserInfoEndpointConfigurer userInfoResponseHandler(
|
||||
AuthenticationSuccessHandler userInfoResponseHandler) {
|
||||
this.userInfoResponseHandler = userInfoResponseHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* and returning the {@link OAuth2Error Error Response}.
|
||||
*
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException}
|
||||
* Sets the {@link AuthenticationFailureHandler} used for handling an
|
||||
* {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error
|
||||
* Response}.
|
||||
* @param errorResponseHandler the {@link AuthenticationFailureHandler} used for
|
||||
* handling an {@link OAuth2AuthenticationException}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
* @since 0.4.0
|
||||
*/
|
||||
@@ -161,19 +179,23 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
|
||||
* to an instance of {@link OidcUserInfo} for the UserInfo response.
|
||||
* Sets the {@link Function} used to extract claims from
|
||||
* {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* for the UserInfo response.
|
||||
*
|
||||
* <p>
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
|
||||
* as well as, the following context attributes:
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the
|
||||
* {@link OidcUserInfoAuthenticationToken}, as well as, the following context
|
||||
* attributes:
|
||||
* <ul>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
|
||||
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the
|
||||
* bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the
|
||||
* {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token
|
||||
* used to make the request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from
|
||||
* {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @return the {@link OidcUserInfoEndpointConfigurer} for further configuration
|
||||
*/
|
||||
public OidcUserInfoEndpointConfigurer userInfoMapper(
|
||||
@@ -184,7 +206,8 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
|
||||
@Override
|
||||
void init(HttpSecurity httpSecurity) {
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
String userInfoEndpointUri = authorizationServerSettings.getOidcUserInfoEndpoint();
|
||||
this.requestMatcher = new OrRequestMatcher(
|
||||
new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.GET.name()),
|
||||
@@ -195,26 +218,25 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
authenticationProviders.addAll(0, this.authenticationProviders);
|
||||
}
|
||||
this.authenticationProvidersConsumer.accept(authenticationProviders);
|
||||
authenticationProviders.forEach(authenticationProvider ->
|
||||
httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
authenticationProviders.forEach(
|
||||
authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configure(HttpSecurity httpSecurity) {
|
||||
AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
|
||||
AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils
|
||||
.getAuthorizationServerSettings(httpSecurity);
|
||||
|
||||
OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter =
|
||||
new OidcUserInfoEndpointFilter(
|
||||
authenticationManager,
|
||||
authorizationServerSettings.getOidcUserInfoEndpoint());
|
||||
OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter = new OidcUserInfoEndpointFilter(authenticationManager,
|
||||
authorizationServerSettings.getOidcUserInfoEndpoint());
|
||||
List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
|
||||
if (!this.userInfoRequestConverters.isEmpty()) {
|
||||
authenticationConverters.addAll(0, this.userInfoRequestConverters);
|
||||
}
|
||||
this.userInfoRequestConvertersConsumer.accept(authenticationConverters);
|
||||
oidcUserInfoEndpointFilter.setAuthenticationConverter(
|
||||
new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
oidcUserInfoEndpointFilter
|
||||
.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
|
||||
if (this.userInfoResponseHandler != null) {
|
||||
oidcUserInfoEndpointFilter.setAuthenticationSuccessHandler(this.userInfoResponseHandler);
|
||||
}
|
||||
@@ -232,12 +254,10 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
private static List<AuthenticationConverter> createDefaultAuthenticationConverters() {
|
||||
List<AuthenticationConverter> authenticationConverters = new ArrayList<>();
|
||||
|
||||
authenticationConverters.add(
|
||||
(request) -> {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return new OidcUserInfoAuthenticationToken(authentication);
|
||||
}
|
||||
);
|
||||
authenticationConverters.add((request) -> {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
return new OidcUserInfoAuthenticationToken(authentication);
|
||||
});
|
||||
|
||||
return authenticationConverters;
|
||||
}
|
||||
@@ -245,9 +265,8 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur
|
||||
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
|
||||
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
|
||||
|
||||
OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider =
|
||||
new OidcUserInfoAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider = new OidcUserInfoAuthenticationProvider(
|
||||
OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity));
|
||||
if (this.userInfoMapper != null) {
|
||||
oidcUserInfoAuthenticationProvider.setUserInfoMapper(this.userInfoMapper);
|
||||
}
|
||||
|
||||
@@ -29,14 +29,12 @@ public interface AuthorizationServerContext {
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the Authorization Server's issuer identifier.
|
||||
*
|
||||
* @return the {@code URL} of the Authorization Server's issuer identifier
|
||||
*/
|
||||
String getIssuer();
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationServerSettings}.
|
||||
*
|
||||
* @return the {@link AuthorizationServerSettings}
|
||||
*/
|
||||
AuthorizationServerSettings getAuthorizationServerSettings();
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
package org.springframework.security.oauth2.server.authorization.context;
|
||||
|
||||
/**
|
||||
* A holder of the {@link AuthorizationServerContext} that associates it with the current thread using a {@code ThreadLocal}.
|
||||
* A holder of the {@link AuthorizationServerContext} that associates it with the current
|
||||
* thread using a {@code ThreadLocal}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.2.2
|
||||
* @see AuthorizationServerContext
|
||||
*/
|
||||
public final class AuthorizationServerContextHolder {
|
||||
|
||||
private static final ThreadLocal<AuthorizationServerContext> holder = new ThreadLocal<>();
|
||||
|
||||
private AuthorizationServerContextHolder() {
|
||||
@@ -30,7 +32,6 @@ public final class AuthorizationServerContextHolder {
|
||||
|
||||
/**
|
||||
* Returns the {@link AuthorizationServerContext} bound to the current thread.
|
||||
*
|
||||
* @return the {@link AuthorizationServerContext}
|
||||
*/
|
||||
public static AuthorizationServerContext getContext() {
|
||||
@@ -39,13 +40,13 @@ public final class AuthorizationServerContextHolder {
|
||||
|
||||
/**
|
||||
* Bind the given {@link AuthorizationServerContext} to the current thread.
|
||||
*
|
||||
* @param authorizationServerContext the {@link AuthorizationServerContext}
|
||||
*/
|
||||
public static void setContext(AuthorizationServerContext authorizationServerContext) {
|
||||
if (authorizationServerContext == null) {
|
||||
resetContext();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
holder.set(authorizationServerContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,20 +28,20 @@ public interface Context {
|
||||
|
||||
/**
|
||||
* Returns the value of the attribute associated to the key.
|
||||
*
|
||||
* @param key the key for the attribute
|
||||
* @param <V> the type of the value for the attribute
|
||||
* @return the value of the attribute associated to the key, or {@code null} if not available
|
||||
* @return the value of the attribute associated to the key, or {@code null} if not
|
||||
* available
|
||||
*/
|
||||
@Nullable
|
||||
<V> V get(Object key);
|
||||
|
||||
/**
|
||||
* Returns the value of the attribute associated to the key.
|
||||
*
|
||||
* @param key the key for the attribute
|
||||
* @param <V> the type of the value for the attribute
|
||||
* @return the value of the attribute associated to the key, or {@code null} if not available or not of the specified type
|
||||
* @return the value of the attribute associated to the key, or {@code null} if not
|
||||
* available or not of the specified type
|
||||
*/
|
||||
@Nullable
|
||||
default <V> V get(Class<V> key) {
|
||||
@@ -51,10 +51,11 @@ public interface Context {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if an attribute associated to the key exists, {@code false} otherwise.
|
||||
*
|
||||
* Returns {@code true} if an attribute associated to the key exists, {@code false}
|
||||
* otherwise.
|
||||
* @param key the key for the attribute
|
||||
* @return {@code true} if an attribute associated to the key exists, {@code false} otherwise
|
||||
* @return {@code true} if an attribute associated to the key exists, {@code false}
|
||||
* otherwise
|
||||
*/
|
||||
boolean hasKey(Object key);
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link HttpMessageConverter} for an {@link OAuth2AuthorizationServerMetadata OAuth 2.0 Authorization Server Metadata Response}.
|
||||
* A {@link HttpMessageConverter} for an {@link OAuth2AuthorizationServerMetadata OAuth
|
||||
* 2.0 Authorization Server Metadata Response}.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @since 0.1.1
|
||||
@@ -48,12 +49,14 @@ import org.springframework.util.Assert;
|
||||
public class OAuth2AuthorizationServerMetadataHttpMessageConverter
|
||||
extends AbstractHttpMessageConverter<OAuth2AuthorizationServerMetadata> {
|
||||
|
||||
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP =
|
||||
new ParameterizedTypeReference<Map<String, Object>>() {};
|
||||
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
};
|
||||
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter();
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters
|
||||
.getJsonMessageConverter();
|
||||
|
||||
private Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter = new OAuth2AuthorizationServerMetadataConverter();
|
||||
|
||||
private Converter<OAuth2AuthorizationServerMetadata, Map<String, Object>> authorizationServerMetadataParametersConverter = OAuth2AuthorizationServerMetadata::getClaims;
|
||||
|
||||
public OAuth2AuthorizationServerMetadataHttpMessageConverter() {
|
||||
@@ -67,65 +70,74 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected OAuth2AuthorizationServerMetadata readInternal(Class<? extends OAuth2AuthorizationServerMetadata> clazz, HttpInputMessage inputMessage)
|
||||
throws HttpMessageNotReadableException {
|
||||
protected OAuth2AuthorizationServerMetadata readInternal(Class<? extends OAuth2AuthorizationServerMetadata> clazz,
|
||||
HttpInputMessage inputMessage) throws HttpMessageNotReadableException {
|
||||
try {
|
||||
Map<String, Object> authorizationServerMetadataParameters =
|
||||
(Map<String, Object>) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, inputMessage);
|
||||
Map<String, Object> authorizationServerMetadataParameters = (Map<String, Object>) this.jsonMessageConverter
|
||||
.read(STRING_OBJECT_MAP.getType(), null, inputMessage);
|
||||
return this.authorizationServerMetadataConverter.convert(authorizationServerMetadataParameters);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HttpMessageNotReadableException(
|
||||
"An error occurred reading the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex, inputMessage);
|
||||
"An error occurred reading the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex,
|
||||
inputMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(OAuth2AuthorizationServerMetadata authorizationServerMetadata, HttpOutputMessage outputMessage)
|
||||
throws HttpMessageNotWritableException {
|
||||
protected void writeInternal(OAuth2AuthorizationServerMetadata authorizationServerMetadata,
|
||||
HttpOutputMessage outputMessage) throws HttpMessageNotWritableException {
|
||||
try {
|
||||
Map<String, Object> authorizationServerMetadataResponseParameters =
|
||||
this.authorizationServerMetadataParametersConverter.convert(authorizationServerMetadata);
|
||||
this.jsonMessageConverter.write(
|
||||
authorizationServerMetadataResponseParameters,
|
||||
STRING_OBJECT_MAP.getType(),
|
||||
MediaType.APPLICATION_JSON,
|
||||
outputMessage
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
Map<String, Object> authorizationServerMetadataResponseParameters = this.authorizationServerMetadataParametersConverter
|
||||
.convert(authorizationServerMetadata);
|
||||
this.jsonMessageConverter.write(authorizationServerMetadataResponseParameters, STRING_OBJECT_MAP.getType(),
|
||||
MediaType.APPLICATION_JSON, outputMessage);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HttpMessageNotWritableException(
|
||||
"An error occurred writing the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting the OAuth 2.0 Authorization Server Metadata
|
||||
* parameters to an {@link OAuth2AuthorizationServerMetadata}.
|
||||
*
|
||||
* @param authorizationServerMetadataConverter the {@link Converter} used for converting to
|
||||
* an {@link OAuth2AuthorizationServerMetadata}.
|
||||
* Sets the {@link Converter} used for converting the OAuth 2.0 Authorization Server
|
||||
* Metadata parameters to an {@link OAuth2AuthorizationServerMetadata}.
|
||||
* @param authorizationServerMetadataConverter the {@link Converter} used for
|
||||
* converting to an {@link OAuth2AuthorizationServerMetadata}.
|
||||
*/
|
||||
public final void setAuthorizationServerMetadataConverter(Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter) {
|
||||
public final void setAuthorizationServerMetadataConverter(
|
||||
Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter) {
|
||||
Assert.notNull(authorizationServerMetadataConverter, "authorizationServerMetadataConverter cannot be null");
|
||||
this.authorizationServerMetadataConverter = authorizationServerMetadataConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting the {@link OAuth2AuthorizationServerMetadata} to a
|
||||
* {@code Map} representation of the OAuth 2.0 Authorization Server Metadata.
|
||||
*
|
||||
* @param authorizationServerMetadataParametersConverter the {@link Converter} used for converting to a
|
||||
* {@code Map} representation of the OAuth 2.0 Authorization Server Metadata.
|
||||
* Sets the {@link Converter} used for converting the
|
||||
* {@link OAuth2AuthorizationServerMetadata} to a {@code Map} representation of the
|
||||
* OAuth 2.0 Authorization Server Metadata.
|
||||
* @param authorizationServerMetadataParametersConverter the {@link Converter} used
|
||||
* for converting to a {@code Map} representation of the OAuth 2.0 Authorization
|
||||
* Server Metadata.
|
||||
*/
|
||||
public final void setAuthorizationServerMetadataParametersConverter(Converter<OAuth2AuthorizationServerMetadata, Map<String, Object>> authorizationServerMetadataParametersConverter) {
|
||||
Assert.notNull(authorizationServerMetadataParametersConverter, "authorizationServerMetadataParametersConverter cannot be null");
|
||||
public final void setAuthorizationServerMetadataParametersConverter(
|
||||
Converter<OAuth2AuthorizationServerMetadata, Map<String, Object>> authorizationServerMetadataParametersConverter) {
|
||||
Assert.notNull(authorizationServerMetadataParametersConverter,
|
||||
"authorizationServerMetadataParametersConverter cannot be null");
|
||||
this.authorizationServerMetadataParametersConverter = authorizationServerMetadataParametersConverter;
|
||||
}
|
||||
|
||||
private static final class OAuth2AuthorizationServerMetadataConverter implements Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> {
|
||||
private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance();
|
||||
private static final class OAuth2AuthorizationServerMetadataConverter
|
||||
implements Converter<Map<String, Object>, OAuth2AuthorizationServerMetadata> {
|
||||
|
||||
private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService
|
||||
.getSharedInstance();
|
||||
|
||||
private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);
|
||||
|
||||
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);
|
||||
|
||||
private final ClaimTypeConverter claimTypeConverter;
|
||||
|
||||
private OAuth2AuthorizationServerMetadataConverter() {
|
||||
@@ -136,18 +148,27 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
|
||||
Map<String, Converter<Object, ?>> claimConverters = new HashMap<>();
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT,
|
||||
urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, urlConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, collectionStringConverter);
|
||||
claimConverters.put(
|
||||
OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED,
|
||||
collectionStringConverter);
|
||||
this.claimTypeConverter = new ClaimTypeConverter(claimConverters);
|
||||
}
|
||||
|
||||
@@ -160,6 +181,7 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter
|
||||
private static Converter<Object, ?> getConverter(TypeDescriptor targetDescriptor) {
|
||||
return (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,7 +46,8 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link HttpMessageConverter} for an {@link OAuth2TokenIntrospection OAuth 2.0 Token Introspection Response}.
|
||||
* A {@link HttpMessageConverter} for an {@link OAuth2TokenIntrospection OAuth 2.0 Token
|
||||
* Introspection Response}.
|
||||
*
|
||||
* @author Gerardo Roza
|
||||
* @author Joe Grandja
|
||||
@@ -54,14 +55,17 @@ import org.springframework.util.StringUtils;
|
||||
* @see AbstractHttpMessageConverter
|
||||
* @see OAuth2TokenIntrospection
|
||||
*/
|
||||
public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMessageConverter<OAuth2TokenIntrospection> {
|
||||
public class OAuth2TokenIntrospectionHttpMessageConverter
|
||||
extends AbstractHttpMessageConverter<OAuth2TokenIntrospection> {
|
||||
|
||||
private static final ParameterizedTypeReference<Map<String, Object>> STRING_OBJECT_MAP = new ParameterizedTypeReference<Map<String, Object>>() {
|
||||
};
|
||||
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter();
|
||||
private final GenericHttpMessageConverter<Object> jsonMessageConverter = HttpMessageConverters
|
||||
.getJsonMessageConverter();
|
||||
|
||||
private Converter<Map<String, Object>, OAuth2TokenIntrospection> tokenIntrospectionConverter = new MapOAuth2TokenIntrospectionConverter();
|
||||
|
||||
private Converter<OAuth2TokenIntrospection, Map<String, Object>> tokenIntrospectionParametersConverter = new OAuth2TokenIntrospectionMapConverter();
|
||||
|
||||
public OAuth2TokenIntrospectionHttpMessageConverter() {
|
||||
@@ -75,13 +79,14 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected OAuth2TokenIntrospection readInternal(Class<? extends OAuth2TokenIntrospection> clazz, HttpInputMessage inputMessage)
|
||||
throws HttpMessageNotReadableException {
|
||||
protected OAuth2TokenIntrospection readInternal(Class<? extends OAuth2TokenIntrospection> clazz,
|
||||
HttpInputMessage inputMessage) throws HttpMessageNotReadableException {
|
||||
try {
|
||||
Map<String, Object> tokenIntrospectionParameters = (Map<String, Object>) this.jsonMessageConverter
|
||||
.read(STRING_OBJECT_MAP.getType(), null, inputMessage);
|
||||
.read(STRING_OBJECT_MAP.getType(), null, inputMessage);
|
||||
return this.tokenIntrospectionConverter.convert(tokenIntrospectionParameters);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HttpMessageNotReadableException(
|
||||
"An error occurred reading the Token Introspection Response: " + ex.getMessage(), ex, inputMessage);
|
||||
}
|
||||
@@ -92,19 +97,21 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
throws HttpMessageNotWritableException {
|
||||
try {
|
||||
Map<String, Object> tokenIntrospectionResponseParameters = this.tokenIntrospectionParametersConverter
|
||||
.convert(tokenIntrospection);
|
||||
.convert(tokenIntrospection);
|
||||
this.jsonMessageConverter.write(tokenIntrospectionResponseParameters, STRING_OBJECT_MAP.getType(),
|
||||
MediaType.APPLICATION_JSON, outputMessage);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new HttpMessageNotWritableException(
|
||||
"An error occurred writing the Token Introspection Response: " + ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting the Token Introspection Response parameters to an {@link OAuth2TokenIntrospection}.
|
||||
*
|
||||
* @param tokenIntrospectionConverter the {@link Converter} used for converting to an {@link OAuth2TokenIntrospection}
|
||||
* Sets the {@link Converter} used for converting the Token Introspection Response
|
||||
* parameters to an {@link OAuth2TokenIntrospection}.
|
||||
* @param tokenIntrospectionConverter the {@link Converter} used for converting to an
|
||||
* {@link OAuth2TokenIntrospection}
|
||||
*/
|
||||
public final void setTokenIntrospectionConverter(
|
||||
Converter<Map<String, Object>, OAuth2TokenIntrospection> tokenIntrospectionConverter) {
|
||||
@@ -115,9 +122,9 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting an {@link OAuth2TokenIntrospection}
|
||||
* to a {@code Map} representation of the Token Introspection Response parameters.
|
||||
*
|
||||
* @param tokenIntrospectionParametersConverter the {@link Converter} used for converting to a
|
||||
* {@code Map} representation of the Token Introspection Response parameters
|
||||
* @param tokenIntrospectionParametersConverter the {@link Converter} used for
|
||||
* converting to a {@code Map} representation of the Token Introspection Response
|
||||
* parameters
|
||||
*/
|
||||
public final void setTokenIntrospectionParametersConverter(
|
||||
Converter<OAuth2TokenIntrospection, Map<String, Object>> tokenIntrospectionParametersConverter) {
|
||||
@@ -128,12 +135,19 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
private static final class MapOAuth2TokenIntrospectionConverter
|
||||
implements Converter<Map<String, Object>, OAuth2TokenIntrospection> {
|
||||
|
||||
private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance();
|
||||
private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService
|
||||
.getSharedInstance();
|
||||
|
||||
private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class);
|
||||
|
||||
private static final TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class);
|
||||
|
||||
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
private static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class);
|
||||
|
||||
private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class);
|
||||
|
||||
private final ClaimTypeConverter claimTypeConverter;
|
||||
|
||||
private MapOAuth2TokenIntrospectionConverter() {
|
||||
@@ -146,7 +160,8 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
|
||||
Map<String, Converter<Object, ?>> claimConverters = new HashMap<>();
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, booleanConverter);
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.SCOPE, MapOAuth2TokenIntrospectionConverter::convertScope);
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.SCOPE,
|
||||
MapOAuth2TokenIntrospectionConverter::convertScope);
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, stringConverter);
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.USERNAME, stringConverter);
|
||||
claimConverters.put(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, stringConverter);
|
||||
@@ -176,6 +191,7 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
}
|
||||
return Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), " "));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class OAuth2TokenIntrospectionMapConverter
|
||||
@@ -185,7 +201,8 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
public Map<String, Object> convert(OAuth2TokenIntrospection source) {
|
||||
Map<String, Object> responseClaims = new LinkedHashMap<>(source.getClaims());
|
||||
if (!CollectionUtils.isEmpty(source.getScopes())) {
|
||||
responseClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, StringUtils.collectionToDelimitedString(source.getScopes(), " "));
|
||||
responseClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE,
|
||||
StringUtils.collectionToDelimitedString(source.getScopes(), " "));
|
||||
}
|
||||
if (source.getExpiresAt() != null) {
|
||||
responseClaims.put(OAuth2TokenIntrospectionClaimNames.EXP, source.getExpiresAt().getEpochSecond());
|
||||
@@ -198,6 +215,7 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe
|
||||
}
|
||||
return responseClaims;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,4 +31,5 @@ import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
abstract class JwsAlgorithmMixin {
|
||||
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer<OAut
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Builder getBuilder(JsonParser parser,
|
||||
AuthorizationGrantType authorizationGrantType) throws JsonParseException {
|
||||
private Builder getBuilder(JsonParser parser, AuthorizationGrantType authorizationGrantType)
|
||||
throws JsonParseException {
|
||||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(authorizationGrantType)) {
|
||||
return OAuth2AuthorizationRequest.authorizationCode();
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
|
||||
/**
|
||||
* A {@link ClaimAccessor} for the "claims" that are contained
|
||||
* in the OpenID Client Registration Request and Response.
|
||||
* A {@link ClaimAccessor} for the "claims" that are contained in the OpenID Client
|
||||
* Registration Request and Response.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
@@ -36,14 +36,17 @@ import org.springframework.security.oauth2.jwt.Jwt;
|
||||
* @see ClaimAccessor
|
||||
* @see OidcClientMetadataClaimNames
|
||||
* @see OidcClientRegistration
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata">2. Client Metadata</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1. Client Registration Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata">2.
|
||||
* Client Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1.
|
||||
* Client Registration Metadata</a>
|
||||
*/
|
||||
public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
|
||||
/**
|
||||
* Returns the Client Identifier {@code (client_id)}.
|
||||
*
|
||||
* @return the Client Identifier
|
||||
*/
|
||||
default String getClientId() {
|
||||
@@ -51,8 +54,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which the Client Identifier was issued {@code (client_id_issued_at)}.
|
||||
*
|
||||
* Returns the time at which the Client Identifier was issued
|
||||
* {@code (client_id_issued_at)}.
|
||||
* @return the time at which the Client Identifier was issued
|
||||
*/
|
||||
default Instant getClientIdIssuedAt() {
|
||||
@@ -61,7 +64,6 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
|
||||
/**
|
||||
* Returns the Client Secret {@code (client_secret)}.
|
||||
*
|
||||
* @return the Client Secret
|
||||
*/
|
||||
default String getClientSecret() {
|
||||
@@ -69,8 +71,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which the {@code client_secret} will expire {@code (client_secret_expires_at)}.
|
||||
*
|
||||
* Returns the time at which the {@code client_secret} will expire
|
||||
* {@code (client_secret_expires_at)}.
|
||||
* @return the time at which the {@code client_secret} will expire
|
||||
*/
|
||||
default Instant getClientSecretExpiresAt() {
|
||||
@@ -78,8 +80,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the Client to be presented to the End-User {@code (client_name)}.
|
||||
*
|
||||
* Returns the name of the Client to be presented to the End-User
|
||||
* {@code (client_name)}.
|
||||
* @return the name of the Client to be presented to the End-User
|
||||
*/
|
||||
default String getClientName() {
|
||||
@@ -87,8 +89,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the redirection {@code URI} values used by the Client {@code (redirect_uris)}.
|
||||
*
|
||||
* Returns the redirection {@code URI} values used by the Client
|
||||
* {@code (redirect_uris)}.
|
||||
* @return the redirection {@code URI} values used by the Client
|
||||
*/
|
||||
default List<String> getRedirectUris() {
|
||||
@@ -96,10 +98,10 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the post logout redirection {@code URI} values used by the Client {@code (post_logout_redirect_uris)}.
|
||||
* The {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
*
|
||||
* Returns the post logout redirection {@code URI} values used by the Client
|
||||
* {@code (post_logout_redirect_uris)}. The {@code post_logout_redirect_uri} parameter
|
||||
* is used by the client when requesting that the End-User's User Agent be redirected
|
||||
* to after a logout has been performed.
|
||||
* @return the post logout redirection {@code URI} values used by the Client
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -108,8 +110,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication method used by the Client for the Token Endpoint {@code (token_endpoint_auth_method)}.
|
||||
*
|
||||
* Returns the authentication method used by the Client for the Token Endpoint
|
||||
* {@code (token_endpoint_auth_method)}.
|
||||
* @return the authentication method used by the Client for the Token Endpoint
|
||||
*/
|
||||
default String getTokenEndpointAuthenticationMethod() {
|
||||
@@ -117,11 +119,13 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate
|
||||
* the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and
|
||||
* {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods {@code (token_endpoint_auth_signing_alg)}.
|
||||
*
|
||||
* @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate the Client at the Token Endpoint
|
||||
* Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the
|
||||
* {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the
|
||||
* {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and
|
||||
* {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}
|
||||
* authentication methods {@code (token_endpoint_auth_signing_alg)}.
|
||||
* @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the
|
||||
* {@link Jwt JWT} used to authenticate the Client at the Token Endpoint
|
||||
* @since 0.2.2
|
||||
*/
|
||||
default String getTokenEndpointAuthenticationSigningAlgorithm() {
|
||||
@@ -129,27 +133,30 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using {@code (grant_types)}.
|
||||
*
|
||||
* @return the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using
|
||||
* Returns the OAuth 2.0 {@code grant_type} values that the Client will restrict
|
||||
* itself to using {@code (grant_types)}.
|
||||
* @return the OAuth 2.0 {@code grant_type} values that the Client will restrict
|
||||
* itself to using
|
||||
*/
|
||||
default List<String> getGrantTypes() {
|
||||
return getClaimAsStringList(OidcClientMetadataClaimNames.GRANT_TYPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using {@code (response_types)}.
|
||||
*
|
||||
* @return the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using
|
||||
* Returns the OAuth 2.0 {@code response_type} values that the Client will restrict
|
||||
* itself to using {@code (response_types)}.
|
||||
* @return the OAuth 2.0 {@code response_type} values that the Client will restrict
|
||||
* itself to using
|
||||
*/
|
||||
default List<String> getResponseTypes() {
|
||||
return getClaimAsStringList(OidcClientMetadataClaimNames.RESPONSE_TYPES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OAuth 2.0 {@code scope} values that the Client will restrict itself to using {@code (scope)}.
|
||||
*
|
||||
* @return the OAuth 2.0 {@code scope} values that the Client will restrict itself to using
|
||||
* Returns the OAuth 2.0 {@code scope} values that the Client will restrict itself to
|
||||
* using {@code (scope)}.
|
||||
* @return the OAuth 2.0 {@code scope} values that the Client will restrict itself to
|
||||
* using
|
||||
*/
|
||||
default List<String> getScopes() {
|
||||
return getClaimAsStringList(OidcClientMetadataClaimNames.SCOPE);
|
||||
@@ -157,7 +164,6 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)}.
|
||||
*
|
||||
* @return the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)}
|
||||
* @since 0.2.2
|
||||
*/
|
||||
@@ -166,18 +172,21 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client {@code (id_token_signed_response_alg)}.
|
||||
*
|
||||
* @return the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client
|
||||
* Returns the {@link SignatureAlgorithm JWS} algorithm required for signing the
|
||||
* {@link OidcIdToken ID Token} issued to the Client
|
||||
* {@code (id_token_signed_response_alg)}.
|
||||
* @return the {@link SignatureAlgorithm JWS} algorithm required for signing the
|
||||
* {@link OidcIdToken ID Token} issued to the Client
|
||||
*/
|
||||
default String getIdTokenSignedResponseAlgorithm() {
|
||||
return getClaimAsString(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Registration Access Token that can be used at the Client Configuration Endpoint.
|
||||
*
|
||||
* @return the Registration Access Token that can be used at the Client Configuration Endpoint
|
||||
* Returns the Registration Access Token that can be used at the Client Configuration
|
||||
* Endpoint.
|
||||
* @return the Registration Access Token that can be used at the Client Configuration
|
||||
* Endpoint
|
||||
* @since 0.2.1
|
||||
*/
|
||||
default String getRegistrationAccessToken() {
|
||||
@@ -185,9 +194,10 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used.
|
||||
*
|
||||
* @return the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
|
||||
* Returns the {@code URL} of the Client Configuration Endpoint where the Registration
|
||||
* Access Token can be used.
|
||||
* @return the {@code URL} of the Client Configuration Endpoint where the Registration
|
||||
* Access Token can be used
|
||||
* @since 0.2.1
|
||||
*/
|
||||
default URL getRegistrationClientUrl() {
|
||||
|
||||
@@ -27,8 +27,12 @@ import org.springframework.security.oauth2.jwt.Jwt;
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.1
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata">2. Client Metadata</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1. Client Registration Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata">2.
|
||||
* Client Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1.
|
||||
* Client Registration Metadata</a>
|
||||
*/
|
||||
public final class OidcClientMetadataClaimNames {
|
||||
|
||||
@@ -48,7 +52,8 @@ public final class OidcClientMetadataClaimNames {
|
||||
public static final String CLIENT_SECRET = "client_secret";
|
||||
|
||||
/**
|
||||
* {@code client_secret_expires_at} - the time at which the {@code client_secret} will expire or 0 if it will not expire
|
||||
* {@code client_secret_expires_at} - the time at which the {@code client_secret} will
|
||||
* expire or 0 if it will not expire
|
||||
*/
|
||||
public static final String CLIENT_SECRET_EXPIRES_AT = "client_secret_expires_at";
|
||||
|
||||
@@ -63,38 +68,45 @@ public final class OidcClientMetadataClaimNames {
|
||||
public static final String REDIRECT_URIS = "redirect_uris";
|
||||
|
||||
/**
|
||||
* {@code post_logout_redirect_uris} - the post logout redirection {@code URI} values used by the Client.
|
||||
* The {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
* {@code post_logout_redirect_uris} - the post logout redirection {@code URI} values
|
||||
* used by the Client. The {@code post_logout_redirect_uri} parameter is used by the
|
||||
* client when requesting that the End-User's User Agent be redirected to after a
|
||||
* logout has been performed.
|
||||
* @since 1.1
|
||||
*/
|
||||
public static final String POST_LOGOUT_REDIRECT_URIS = "post_logout_redirect_uris";
|
||||
|
||||
/**
|
||||
* {@code token_endpoint_auth_method} - the authentication method used by the Client for the Token Endpoint
|
||||
* {@code token_endpoint_auth_method} - the authentication method used by the Client
|
||||
* for the Token Endpoint
|
||||
*/
|
||||
public static final String TOKEN_ENDPOINT_AUTH_METHOD = "token_endpoint_auth_method";
|
||||
|
||||
/**
|
||||
* {@code token_endpoint_auth_signing_alg} - the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT}
|
||||
* used to authenticate the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and
|
||||
* {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods
|
||||
* {@code token_endpoint_auth_signing_alg} - the {@link JwsAlgorithm JWS} algorithm
|
||||
* that must be used for signing the {@link Jwt JWT} used to authenticate the Client
|
||||
* at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT
|
||||
* private_key_jwt} and {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT
|
||||
* client_secret_jwt} authentication methods
|
||||
* @since 0.2.2
|
||||
*/
|
||||
public static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "token_endpoint_auth_signing_alg";
|
||||
|
||||
/**
|
||||
* {@code grant_types} - the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using
|
||||
* {@code grant_types} - the OAuth 2.0 {@code grant_type} values that the Client will
|
||||
* restrict itself to using
|
||||
*/
|
||||
public static final String GRANT_TYPES = "grant_types";
|
||||
|
||||
/**
|
||||
* {@code response_types} - the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using
|
||||
* {@code response_types} - the OAuth 2.0 {@code response_type} values that the Client
|
||||
* will restrict itself to using
|
||||
*/
|
||||
public static final String RESPONSE_TYPES = "response_types";
|
||||
|
||||
/**
|
||||
* {@code scope} - a space-separated list of OAuth 2.0 {@code scope} values that the Client will restrict itself to using
|
||||
* {@code scope} - a space-separated list of OAuth 2.0 {@code scope} values that the
|
||||
* Client will restrict itself to using
|
||||
*/
|
||||
public static final String SCOPE = "scope";
|
||||
|
||||
@@ -105,18 +117,21 @@ public final class OidcClientMetadataClaimNames {
|
||||
public static final String JWKS_URI = "jwks_uri";
|
||||
|
||||
/**
|
||||
* {@code id_token_signed_response_alg} - the {@link JwsAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client
|
||||
* {@code id_token_signed_response_alg} - the {@link JwsAlgorithm JWS} algorithm
|
||||
* required for signing the {@link OidcIdToken ID Token} issued to the Client
|
||||
*/
|
||||
public static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "id_token_signed_response_alg";
|
||||
|
||||
/**
|
||||
* {@code registration_access_token} - the Registration Access Token that can be used at the Client Configuration Endpoint
|
||||
* {@code registration_access_token} - the Registration Access Token that can be used
|
||||
* at the Client Configuration Endpoint
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public static final String REGISTRATION_ACCESS_TOKEN = "registration_access_token";
|
||||
|
||||
/**
|
||||
* {@code registration_client_uri} - the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
|
||||
* {@code registration_client_uri} - the {@code URL} of the Client Configuration
|
||||
* Endpoint where the Registration Access Token can be used
|
||||
* @since 0.2.1
|
||||
*/
|
||||
public static final String REGISTRATION_CLIENT_URI = "registration_client_uri";
|
||||
|
||||
@@ -35,21 +35,29 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of an OpenID Client Registration Request and Response,
|
||||
* which is sent to and returned from the Client Registration Endpoint,
|
||||
* and contains a set of claims about the Client's Registration information.
|
||||
* The claims are defined by the OpenID Connect Dynamic Client Registration 1.0 specification.
|
||||
* A representation of an OpenID Client Registration Request and Response, which is sent
|
||||
* to and returned from the Client Registration Endpoint, and contains a set of claims
|
||||
* about the Client's Registration information. The claims are defined by the OpenID
|
||||
* Connect Dynamic Client Registration 1.0 specification.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.1
|
||||
* @see OidcClientMetadataClaimAccessor
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest">3.1. Client Registration Request</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse">3.2. Client Registration Response</a>
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1. Client Registration Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationRequest">3.1.
|
||||
* Client Registration Request</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationResponse">3.2.
|
||||
* Client Registration Response</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ClientMetadata">3.1.
|
||||
* Client Registration Metadata</a>
|
||||
*/
|
||||
public final class OidcClientRegistration implements OidcClientMetadataClaimAccessor, Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Map<String, Object> claims;
|
||||
|
||||
private OidcClientRegistration(Map<String, Object> claims) {
|
||||
@@ -59,7 +67,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Returns the metadata as claims.
|
||||
*
|
||||
* @return a {@code Map} of the metadata as claims
|
||||
*/
|
||||
@Override
|
||||
@@ -69,7 +76,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with empty claims.
|
||||
*
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder builder() {
|
||||
@@ -78,19 +84,18 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided claims.
|
||||
*
|
||||
* @param claims the claims to initialize the builder
|
||||
*/
|
||||
public static Builder withClaims(Map<String, Object> claims) {
|
||||
Assert.notEmpty(claims, "claims cannot be empty");
|
||||
return new Builder()
|
||||
.claims(c -> c.putAll(claims));
|
||||
return new Builder().claims(c -> c.putAll(claims));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helps configure an {@link OidcClientRegistration}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private final Map<String, Object> claims = new LinkedHashMap<>();
|
||||
|
||||
private Builder() {
|
||||
@@ -98,7 +103,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Sets the Client Identifier, REQUIRED.
|
||||
*
|
||||
* @param clientId the Client Identifier
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -108,7 +112,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Sets the time at which the Client Identifier was issued, OPTIONAL.
|
||||
*
|
||||
* @param clientIdIssuedAt the time at which the Client Identifier was issued
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -118,7 +121,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Sets the Client Secret, OPTIONAL.
|
||||
*
|
||||
* @param clientSecret the Client Secret
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -127,9 +129,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time at which the {@code client_secret} will expire or {@code null} if it will not expire, REQUIRED if {@code client_secret} was issued.
|
||||
*
|
||||
* @param clientSecretExpiresAt the time at which the {@code client_secret} will expire or {@code null} if it will not expire
|
||||
* Sets the time at which the {@code client_secret} will expire or {@code null} if
|
||||
* it will not expire, REQUIRED if {@code client_secret} was issued.
|
||||
* @param clientSecretExpiresAt the time at which the {@code client_secret} will
|
||||
* expire or {@code null} if it will not expire
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder clientSecretExpiresAt(Instant clientSecretExpiresAt) {
|
||||
@@ -138,7 +141,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Sets the name of the Client to be presented to the End-User, OPTIONAL.
|
||||
*
|
||||
* @param clientName the name of the Client to be presented to the End-User
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -148,7 +150,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Add the redirection {@code URI} used by the Client, REQUIRED.
|
||||
*
|
||||
* @param redirectUri the redirection {@code URI} used by the Client
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -160,8 +161,8 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
/**
|
||||
* A {@code Consumer} of the redirection {@code URI} values used by the Client,
|
||||
* allowing the ability to add, replace, or remove, REQUIRED.
|
||||
*
|
||||
* @param redirectUrisConsumer a {@code Consumer} of the redirection {@code URI} values used by the Client
|
||||
* @param redirectUrisConsumer a {@code Consumer} of the redirection {@code URI}
|
||||
* values used by the Client
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder redirectUris(Consumer<List<String>> redirectUrisConsumer) {
|
||||
@@ -170,11 +171,12 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the post logout redirection {@code URI} used by the Client, OPTIONAL.
|
||||
* The {@code post_logout_redirect_uri} parameter is used by the client when requesting
|
||||
* that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
*
|
||||
* @param postLogoutRedirectUri the post logout redirection {@code URI} used by the Client
|
||||
* Add the post logout redirection {@code URI} used by the Client, OPTIONAL. The
|
||||
* {@code post_logout_redirect_uri} parameter is used by the client when
|
||||
* requesting that the End-User's User Agent be redirected to after a logout has
|
||||
* been performed.
|
||||
* @param postLogoutRedirectUri the post logout redirection {@code URI} used by
|
||||
* the Client
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -184,10 +186,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the post logout redirection {@code URI} values used by the Client,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param postLogoutRedirectUrisConsumer a {@code Consumer} of the post logout redirection {@code URI} values used by the Client
|
||||
* A {@code Consumer} of the post logout redirection {@code URI} values used by
|
||||
* the Client, allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
* @param postLogoutRedirectUrisConsumer a {@code Consumer} of the post logout
|
||||
* redirection {@code URI} values used by the Client
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -197,9 +199,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication method used by the Client for the Token Endpoint, OPTIONAL.
|
||||
*
|
||||
* @param tokenEndpointAuthenticationMethod the authentication method used by the Client for the Token Endpoint
|
||||
* Sets the authentication method used by the Client for the Token Endpoint,
|
||||
* OPTIONAL.
|
||||
* @param tokenEndpointAuthenticationMethod the authentication method used by the
|
||||
* Client for the Token Endpoint
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder tokenEndpointAuthenticationMethod(String tokenEndpointAuthenticationMethod) {
|
||||
@@ -207,12 +210,14 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate
|
||||
* the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and
|
||||
* {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods, OPTIONAL.
|
||||
|
||||
* @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT}
|
||||
* used to authenticate the Client at the Token Endpoint
|
||||
* Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the
|
||||
* {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the
|
||||
* {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and
|
||||
* {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt}
|
||||
* authentication methods, OPTIONAL.
|
||||
* @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm
|
||||
* that must be used for signing the {@link Jwt JWT} used to authenticate the
|
||||
* Client at the Token Endpoint
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 0.2.2
|
||||
*/
|
||||
@@ -221,9 +226,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the OAuth 2.0 {@code grant_type} that the Client will restrict itself to using, OPTIONAL.
|
||||
*
|
||||
* @param grantType the OAuth 2.0 {@code grant_type} that the Client will restrict itself to using
|
||||
* Add the OAuth 2.0 {@code grant_type} that the Client will restrict itself to
|
||||
* using, OPTIONAL.
|
||||
* @param grantType the OAuth 2.0 {@code grant_type} that the Client will restrict
|
||||
* itself to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder grantType(String grantType) {
|
||||
@@ -232,10 +238,11 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client
|
||||
* will restrict itself to using, allowing the ability to add, replace, or remove,
|
||||
* OPTIONAL.
|
||||
* @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0
|
||||
* {@code grant_type} values that the Client will restrict itself to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder grantTypes(Consumer<List<String>> grantTypesConsumer) {
|
||||
@@ -244,9 +251,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the OAuth 2.0 {@code response_type} that the Client will restrict itself to using, OPTIONAL.
|
||||
*
|
||||
* @param responseType the OAuth 2.0 {@code response_type} that the Client will restrict itself to using
|
||||
* Add the OAuth 2.0 {@code response_type} that the Client will restrict itself to
|
||||
* using, OPTIONAL.
|
||||
* @param responseType the OAuth 2.0 {@code response_type} that the Client will
|
||||
* restrict itself to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder responseType(String responseType) {
|
||||
@@ -255,21 +263,23 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code response_type} values that the
|
||||
* Client will restrict itself to using, allowing the ability to add, replace, or
|
||||
* remove, OPTIONAL.
|
||||
* @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0
|
||||
* {@code response_type} values that the Client will restrict itself to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder responseTypes(Consumer<List<String>> responseTypesConsumer) {
|
||||
public Builder responseTypes(Consumer<List<String>> responseTypesConsumer) {
|
||||
acceptClaimValues(OidcClientMetadataClaimNames.RESPONSE_TYPES, responseTypesConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the OAuth 2.0 {@code scope} that the Client will restrict itself to using, OPTIONAL.
|
||||
*
|
||||
* @param scope the OAuth 2.0 {@code scope} that the Client will restrict itself to using
|
||||
* Add the OAuth 2.0 {@code scope} that the Client will restrict itself to using,
|
||||
* OPTIONAL.
|
||||
* @param scope the OAuth 2.0 {@code scope} that the Client will restrict itself
|
||||
* to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder scope(String scope) {
|
||||
@@ -278,20 +288,20 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will restrict itself to using,
|
||||
* allowing the ability to add, replace, or remove, OPTIONAL.
|
||||
*
|
||||
* @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will restrict itself to using
|
||||
* A {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will
|
||||
* restrict itself to using, allowing the ability to add, replace, or remove,
|
||||
* OPTIONAL.
|
||||
* @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values
|
||||
* that the Client will restrict itself to using
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder scopes(Consumer<List<String>> scopesConsumer) {
|
||||
public Builder scopes(Consumer<List<String>> scopesConsumer) {
|
||||
acceptClaimValues(OidcClientMetadataClaimNames.SCOPE, scopesConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code URL} for the Client's JSON Web Key Set, OPTIONAL.
|
||||
*
|
||||
* @param jwkSetUrl the {@code URL} for the Client's JSON Web Key Set
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 0.2.2
|
||||
@@ -301,9 +311,11 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client, OPTIONAL.
|
||||
*
|
||||
* @param idTokenSignedResponseAlgorithm the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client
|
||||
* Sets the {@link SignatureAlgorithm JWS} algorithm required for signing the
|
||||
* {@link OidcIdToken ID Token} issued to the Client, OPTIONAL.
|
||||
* @param idTokenSignedResponseAlgorithm the {@link SignatureAlgorithm JWS}
|
||||
* algorithm required for signing the {@link OidcIdToken ID Token} issued to the
|
||||
* Client
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder idTokenSignedResponseAlgorithm(String idTokenSignedResponseAlgorithm) {
|
||||
@@ -311,9 +323,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Registration Access Token that can be used at the Client Configuration Endpoint, OPTIONAL.
|
||||
*
|
||||
* @param registrationAccessToken the Registration Access Token that can be used at the Client Configuration Endpoint
|
||||
* Sets the Registration Access Token that can be used at the Client Configuration
|
||||
* Endpoint, OPTIONAL.
|
||||
* @param registrationAccessToken the Registration Access Token that can be used
|
||||
* at the Client Configuration Endpoint
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 0.2.1
|
||||
*/
|
||||
@@ -322,9 +335,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used, OPTIONAL.
|
||||
*
|
||||
* @param registrationClientUrl the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used
|
||||
* Sets the {@code URL} of the Client Configuration Endpoint where the
|
||||
* Registration Access Token can be used, OPTIONAL.
|
||||
* @param registrationClientUrl the {@code URL} of the Client Configuration
|
||||
* Endpoint where the Registration Access Token can be used
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 0.2.1
|
||||
*/
|
||||
@@ -334,8 +348,7 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
/**
|
||||
* Sets the claim.
|
||||
*
|
||||
* @param name the claim name
|
||||
* @param name the claim name
|
||||
* @param value the claim value
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -349,7 +362,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
/**
|
||||
* Provides access to every {@link #claim(String, Object)} declared so far
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param claimsConsumer a {@code Consumer} of the claims
|
||||
* @return the {@link Builder} for further configurations
|
||||
*/
|
||||
@@ -361,9 +373,7 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
/**
|
||||
* Validate the claims and build the {@link OidcClientRegistration}.
|
||||
* <p>
|
||||
* The following claims are REQUIRED:
|
||||
* {@code client_id}, {@code redirect_uris}.
|
||||
*
|
||||
* The following claims are REQUIRED: {@code client_id}, {@code redirect_uris}.
|
||||
* @return the {@link OidcClientRegistration}
|
||||
*/
|
||||
public OidcClientRegistration build() {
|
||||
@@ -372,34 +382,47 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null ||
|
||||
this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET) != null) {
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null
|
||||
|| this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET) != null) {
|
||||
Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID), "client_id cannot be null");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null) {
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT), "client_id_issued_at must be of type Instant");
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT),
|
||||
"client_id_issued_at must be of type Instant");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT) != null) {
|
||||
Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET), "client_secret cannot be null");
|
||||
Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT), "client_secret_expires_at must be of type Instant");
|
||||
Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET),
|
||||
"client_secret cannot be null");
|
||||
Assert.isInstanceOf(Instant.class,
|
||||
this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT),
|
||||
"client_secret_expires_at must be of type Instant");
|
||||
}
|
||||
Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris cannot be null");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris cannot be empty");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS),
|
||||
"redirect_uris must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS),
|
||||
"redirect_uris cannot be empty");
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS) != null) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), "post_logout_redirect_uris must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), "post_logout_redirect_uris cannot be empty");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS),
|
||||
"post_logout_redirect_uris must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS),
|
||||
"post_logout_redirect_uris cannot be empty");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES) != null) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), "grant_types must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), "grant_types cannot be empty");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES),
|
||||
"grant_types must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES),
|
||||
"grant_types cannot be empty");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES) != null) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), "response_types must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), "response_types cannot be empty");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES),
|
||||
"response_types must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES),
|
||||
"response_types cannot be empty");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.SCOPE) != null) {
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.SCOPE), "scope must be of type List");
|
||||
Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.SCOPE),
|
||||
"scope must be of type List");
|
||||
Assert.notEmpty((List<?>) this.claims.get(OidcClientMetadataClaimNames.SCOPE), "scope cannot be empty");
|
||||
}
|
||||
if (this.claims.get(OidcClientMetadataClaimNames.JWKS_URI) != null) {
|
||||
@@ -431,7 +454,8 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce
|
||||
|
||||
try {
|
||||
new URI(url.toString()).toURL();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException(errorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +26,19 @@ import org.springframework.security.oauth2.server.authorization.AbstractOAuth2Au
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A representation of an OpenID Provider Configuration Response,
|
||||
* which is returned from an Issuer's Discovery Endpoint,
|
||||
* and contains a set of claims about the OpenID Provider's configuration.
|
||||
* The claims are defined by the OpenID Connect Discovery 1.0 specification.
|
||||
* A representation of an OpenID Provider Configuration Response, which is returned from
|
||||
* an Issuer's Discovery Endpoint, and contains a set of claims about the OpenID
|
||||
* Provider's configuration. The claims are defined by the OpenID Connect Discovery 1.0
|
||||
* specification.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.0
|
||||
* @see AbstractOAuth2AuthorizationServerMetadata
|
||||
* @see OidcProviderMetadataClaimAccessor
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2. OpenID Provider Configuration Response</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse">4.2.
|
||||
* OpenID Provider Configuration Response</a>
|
||||
*/
|
||||
public final class OidcProviderConfiguration extends AbstractOAuth2AuthorizationServerMetadata
|
||||
implements OidcProviderMetadataClaimAccessor {
|
||||
@@ -47,7 +49,6 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with empty claims.
|
||||
*
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
public static Builder builder() {
|
||||
@@ -56,13 +57,11 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided claims.
|
||||
*
|
||||
* @param claims the claims to initialize the builder
|
||||
*/
|
||||
public static Builder withClaims(Map<String, Object> claims) {
|
||||
Assert.notEmpty(claims, "claims cannot be empty");
|
||||
return new Builder()
|
||||
.claims(c -> c.putAll(claims));
|
||||
return new Builder().claims(c -> c.putAll(claims));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,9 +73,8 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this Subject Type to the collection of {@code subject_types_supported} in the resulting
|
||||
* {@link OidcProviderConfiguration}, REQUIRED.
|
||||
*
|
||||
* Add this Subject Type to the collection of {@code subject_types_supported} in
|
||||
* the resulting {@link OidcProviderConfiguration}, REQUIRED.
|
||||
* @param subjectType the Subject Type that the OpenID Provider supports
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -86,8 +84,8 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the Subject Types(s) allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* A {@code Consumer} of the Subject Types(s) allowing the ability to add,
|
||||
* replace, or remove.
|
||||
* @param subjectTypesConsumer a {@code Consumer} of the Subject Types(s)
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -97,10 +95,11 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
/**
|
||||
* Add this {@link JwsAlgorithm JWS} signing algorithm to the collection of {@code id_token_signing_alg_values_supported}
|
||||
* in the resulting {@link OidcProviderConfiguration}, REQUIRED.
|
||||
*
|
||||
* @param signingAlgorithm the {@link JwsAlgorithm JWS} signing algorithm supported for the {@link OidcIdToken ID Token}
|
||||
* Add this {@link JwsAlgorithm JWS} signing algorithm to the collection of
|
||||
* {@code id_token_signing_alg_values_supported} in the resulting
|
||||
* {@link OidcProviderConfiguration}, REQUIRED.
|
||||
* @param signingAlgorithm the {@link JwsAlgorithm JWS} signing algorithm
|
||||
* supported for the {@link OidcIdToken ID Token}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder idTokenSigningAlgorithm(String signingAlgorithm) {
|
||||
@@ -109,21 +108,23 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the {@link OidcIdToken ID Token}
|
||||
* allowing the ability to add, replace, or remove.
|
||||
*
|
||||
* @param signingAlgorithmsConsumer a {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the {@link OidcIdToken ID Token}
|
||||
* A {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the
|
||||
* {@link OidcIdToken ID Token} allowing the ability to add, replace, or remove.
|
||||
* @param signingAlgorithmsConsumer a {@code Consumer} of the {@link JwsAlgorithm
|
||||
* JWS} signing algorithms for the {@link OidcIdToken ID Token}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
public Builder idTokenSigningAlgorithms(Consumer<List<String>> signingAlgorithmsConsumer) {
|
||||
acceptClaimValues(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, signingAlgorithmsConsumer);
|
||||
acceptClaimValues(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED,
|
||||
signingAlgorithmsConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code userinfo_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL.
|
||||
*
|
||||
* @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
|
||||
* Use this {@code userinfo_endpoint} in the resulting
|
||||
* {@link OidcProviderConfiguration}, OPTIONAL.
|
||||
* @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo
|
||||
* Endpoint
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 0.2.2
|
||||
*/
|
||||
@@ -132,9 +133,10 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@code end_session_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL.
|
||||
*
|
||||
* @param endSessionEndpoint the {@code URL} of the OpenID Connect 1.0 End Session Endpoint
|
||||
* Use this {@code end_session_endpoint} in the resulting
|
||||
* {@link OidcProviderConfiguration}, OPTIONAL.
|
||||
* @param endSessionEndpoint the {@code URL} of the OpenID Connect 1.0 End Session
|
||||
* Endpoint
|
||||
* @return the {@link Builder} for further configuration
|
||||
* @since 1.1
|
||||
*/
|
||||
@@ -145,11 +147,10 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
/**
|
||||
* Validate the claims and build the {@link OidcProviderConfiguration}.
|
||||
* <p>
|
||||
* The following claims are REQUIRED:
|
||||
* {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri},
|
||||
* The following claims are REQUIRED: {@code issuer},
|
||||
* {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri},
|
||||
* {@code response_types_supported}, {@code subject_types_supported} and
|
||||
* {@code id_token_signing_alg_values_supported}.
|
||||
*
|
||||
* @return the {@link OidcProviderConfiguration}
|
||||
*/
|
||||
@Override
|
||||
@@ -162,17 +163,27 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
protected void validate() {
|
||||
super.validate();
|
||||
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.JWKS_URI), "jwksUri cannot be null");
|
||||
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be null");
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be empty");
|
||||
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be null");
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be empty");
|
||||
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),
|
||||
"subjectTypes cannot be null");
|
||||
Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),
|
||||
"subjectTypes must be of type List");
|
||||
Assert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED),
|
||||
"subjectTypes cannot be empty");
|
||||
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),
|
||||
"idTokenSigningAlgorithms cannot be null");
|
||||
Assert.isInstanceOf(List.class,
|
||||
getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),
|
||||
"idTokenSigningAlgorithms must be of type List");
|
||||
Assert.notEmpty(
|
||||
(List<?>) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED),
|
||||
"idTokenSigningAlgorithms cannot be empty");
|
||||
if (getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT),
|
||||
"userInfoEndpoint must be a valid URL");
|
||||
}
|
||||
if (getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT) != null) {
|
||||
validateURL(getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT), "endSessionEndpoint must be a valid URL");
|
||||
validateURL(getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT),
|
||||
"endSessionEndpoint must be a valid URL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,4 +205,5 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimAccessor;
|
||||
|
||||
/**
|
||||
* A {@link ClaimAccessor} for the "claims" that can be returned
|
||||
* in the OpenID Provider Configuration Response.
|
||||
* A {@link ClaimAccessor} for the "claims" that can be returned in the OpenID Provider
|
||||
* Configuration Response.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
* @author Joe Grandja
|
||||
@@ -35,13 +35,14 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
* @see OAuth2AuthorizationServerMetadataClaimAccessor
|
||||
* @see OidcProviderMetadataClaimNames
|
||||
* @see OidcProviderConfiguration
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
|
||||
* Provider Metadata</a>
|
||||
*/
|
||||
public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationServerMetadataClaimAccessor {
|
||||
|
||||
/**
|
||||
* Returns the Subject Identifier types supported {@code (subject_types_supported)}.
|
||||
*
|
||||
* @return the Subject Identifier types supported
|
||||
*/
|
||||
default List<String> getSubjectTypes() {
|
||||
@@ -49,18 +50,19 @@ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationSe
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token}
|
||||
* to encode the claims in a {@link Jwt} {@code (id_token_signing_alg_values_supported)}.
|
||||
*
|
||||
* @return the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token}
|
||||
* Returns the {@link JwsAlgorithm JWS} signing algorithms supported for the
|
||||
* {@link OidcIdToken ID Token} to encode the claims in a {@link Jwt}
|
||||
* {@code (id_token_signing_alg_values_supported)}.
|
||||
* @return the {@link JwsAlgorithm JWS} signing algorithms supported for the
|
||||
* {@link OidcIdToken ID Token}
|
||||
*/
|
||||
default List<String> getIdTokenSigningAlgorithms() {
|
||||
return getClaimAsStringList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint {@code (userinfo_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
|
||||
* {@code (userinfo_endpoint)}.
|
||||
* @return the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
|
||||
* @since 0.2.2
|
||||
*/
|
||||
@@ -69,8 +71,8 @@ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationSe
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code URL} of the OpenID Connect 1.0 End Session Endpoint {@code (end_session_endpoint)}.
|
||||
*
|
||||
* Returns the {@code URL} of the OpenID Connect 1.0 End Session Endpoint
|
||||
* {@code (end_session_endpoint)}.
|
||||
* @return the {@code URL} of the OpenID Connect 1.0 End Session Endpoint
|
||||
* @since 1.1
|
||||
*/
|
||||
|
||||
@@ -27,7 +27,9 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
||||
* @author Joe Grandja
|
||||
* @since 0.1.0
|
||||
* @see OAuth2AuthorizationServerMetadataClaimNames
|
||||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID Provider Metadata</a>
|
||||
* @see <a target="_blank" href=
|
||||
* "https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata">3. OpenID
|
||||
* Provider Metadata</a>
|
||||
*/
|
||||
public final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationServerMetadataClaimNames {
|
||||
|
||||
@@ -37,18 +39,21 @@ public final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationSer
|
||||
public static final String SUBJECT_TYPES_SUPPORTED = "subject_types_supported";
|
||||
|
||||
/**
|
||||
* {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token}
|
||||
* {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS}
|
||||
* signing algorithms supported for the {@link OidcIdToken ID Token}
|
||||
*/
|
||||
public static final String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = "id_token_signing_alg_values_supported";
|
||||
|
||||
/**
|
||||
* {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
|
||||
* {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo
|
||||
* Endpoint
|
||||
* @since 0.2.2
|
||||
*/
|
||||
public static final String USER_INFO_ENDPOINT = "userinfo_endpoint";
|
||||
|
||||
/**
|
||||
* {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End Session Endpoint
|
||||
* {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End
|
||||
* Session Endpoint
|
||||
* @since 1.1
|
||||
*/
|
||||
public static final String END_SESSION_ENDPOINT = "end_session_endpoint";
|
||||
|
||||
@@ -32,8 +32,7 @@ final class OidcAuthenticationProviderUtils {
|
||||
private OidcAuthenticationProviderUtils() {
|
||||
}
|
||||
|
||||
static <T extends OAuth2Token> OAuth2Authorization invalidate(
|
||||
OAuth2Authorization authorization, T token) {
|
||||
static <T extends OAuth2Token> OAuth2Authorization invalidate(OAuth2Authorization authorization, T token) {
|
||||
|
||||
// @formatter:off
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization)
|
||||
@@ -60,4 +59,5 @@ final class OidcAuthenticationProviderUtils {
|
||||
|
||||
return authorizationBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client Configuration Endpoint.
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client
|
||||
* Configuration Endpoint.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
@@ -53,18 +54,25 @@ import org.springframework.util.StringUtils;
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see OidcClientRegistrationAuthenticationToken
|
||||
* @see OidcClientRegistrationAuthenticationProvider
|
||||
* @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint">4. Client Configuration Endpoint</a>
|
||||
* @see <a href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#ClientConfigurationEndpoint">4.
|
||||
* Client Configuration Endpoint</a>
|
||||
*/
|
||||
public final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
static final String DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE = "client.read";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
@@ -90,27 +98,29 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication =
|
||||
(OidcClientRegistrationAuthenticationToken) authentication;
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication;
|
||||
|
||||
if (!StringUtils.hasText(clientRegistrationAuthentication.getClientId())) {
|
||||
// This is not a Client Configuration Request.
|
||||
// Return null to allow OidcClientRegistrationAuthenticationProvider to handle it.
|
||||
// Return null to allow OidcClientRegistrationAuthenticationProvider to handle
|
||||
// it.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate the "registration" access token
|
||||
AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication.getPrincipal();
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class
|
||||
.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication
|
||||
.getPrincipal();
|
||||
}
|
||||
if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
accessTokenValue, OAuth2TokenType.ACCESS_TOKEN);
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,
|
||||
OAuth2TokenType.ACCESS_TOKEN);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
@@ -133,11 +143,12 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
|
||||
return OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
private OidcClientRegistrationAuthenticationToken findRegistration(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
|
||||
private OidcClientRegistrationAuthenticationToken findRegistration(
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
|
||||
OAuth2Authorization authorization) {
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(
|
||||
clientRegistrationAuthentication.getClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findByClientId(clientRegistrationAuthentication.getClientId());
|
||||
if (registeredClient == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
@@ -161,14 +172,16 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken, Set<String> requiredScope) {
|
||||
private static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken,
|
||||
Set<String> requiredScope) {
|
||||
Collection<String> authorizedScope = Collections.emptySet();
|
||||
if (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {
|
||||
authorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);
|
||||
}
|
||||
if (!authorizedScope.containsAll(requiredScope)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
||||
} else if (authorizedScope.size() != requiredScope.size()) {
|
||||
}
|
||||
else if (authorizedScope.size() != requiredScope.size()) {
|
||||
// Restrict the access token to only contain the required scope
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,8 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client Registration Endpoint.
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client
|
||||
* Registration Endpoint.
|
||||
*
|
||||
* @author Ovidiu Popa
|
||||
* @author Joe Grandja
|
||||
@@ -77,29 +78,41 @@ import org.springframework.util.StringUtils;
|
||||
* @see OidcClientRegistrationAuthenticationToken
|
||||
* @see OidcClientConfigurationAuthenticationProvider
|
||||
* @see PasswordEncoder
|
||||
* @see <a href="https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration">3. Client Registration Endpoint</a>
|
||||
* @see <a href=
|
||||
* "https://openid.net/specs/openid-connect-registration-1_0.html#ClientRegistration">3.
|
||||
* Client Registration Endpoint</a>
|
||||
*/
|
||||
public final class OidcClientRegistrationAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static final String ERROR_URI = "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError";
|
||||
|
||||
private static final String DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE = "client.create";
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
|
||||
|
||||
private Converter<RegisteredClient, OidcClientRegistration> clientRegistrationConverter;
|
||||
|
||||
private Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter;
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
@@ -113,27 +126,29 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication =
|
||||
(OidcClientRegistrationAuthenticationToken) authentication;
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication;
|
||||
|
||||
if (clientRegistrationAuthentication.getClientRegistration() == null) {
|
||||
// This is not a Client Registration Request.
|
||||
// Return null to allow OidcClientConfigurationAuthenticationProvider to handle it.
|
||||
// Return null to allow OidcClientConfigurationAuthenticationProvider to
|
||||
// handle it.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validate the "initial" access token
|
||||
AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication.getPrincipal();
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class
|
||||
.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) clientRegistrationAuthentication
|
||||
.getPrincipal();
|
||||
}
|
||||
if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
|
||||
String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
accessTokenValue, OAuth2TokenType.ACCESS_TOKEN);
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,
|
||||
OAuth2TokenType.ACCESS_TOKEN);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
@@ -157,12 +172,14 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Converter} used for converting an {@link OidcClientRegistration} to a {@link RegisteredClient}.
|
||||
*
|
||||
* @param registeredClientConverter the {@link Converter} used for converting an {@link OidcClientRegistration} to a {@link RegisteredClient}
|
||||
* Sets the {@link Converter} used for converting an {@link OidcClientRegistration} to
|
||||
* a {@link RegisteredClient}.
|
||||
* @param registeredClientConverter the {@link Converter} used for converting an
|
||||
* {@link OidcClientRegistration} to a {@link RegisteredClient}
|
||||
* @since 0.4.0
|
||||
*/
|
||||
public void setRegisteredClientConverter(Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter) {
|
||||
public void setRegisteredClientConverter(
|
||||
Converter<OidcClientRegistration, RegisteredClient> registeredClientConverter) {
|
||||
Assert.notNull(registeredClientConverter, "registeredClientConverter cannot be null");
|
||||
this.registeredClientConverter = registeredClientConverter;
|
||||
}
|
||||
@@ -179,9 +196,10 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link PasswordEncoder} used to encode the {@link RegisteredClient#getClientSecret() client secret}.
|
||||
* If not set, the client secret will be encoded using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.
|
||||
*
|
||||
* Sets the {@link PasswordEncoder} used to encode the
|
||||
* {@link RegisteredClient#getClientSecret() client secret}. If not set, the client
|
||||
* secret will be encoded using
|
||||
* {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}.
|
||||
* @param passwordEncoder the {@link PasswordEncoder} used to encode the client secret
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@@ -190,38 +208,47 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
private OidcClientRegistrationAuthenticationToken registerClient(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
|
||||
private OidcClientRegistrationAuthenticationToken registerClient(
|
||||
OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication,
|
||||
OAuth2Authorization authorization) {
|
||||
|
||||
if (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getRedirectUris())) {
|
||||
throwInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI, OidcClientMetadataClaimNames.REDIRECT_URIS);
|
||||
throwInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI,
|
||||
OidcClientMetadataClaimNames.REDIRECT_URIS);
|
||||
}
|
||||
|
||||
if (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getPostLogoutRedirectUris())) {
|
||||
throwInvalidClientRegistration("invalid_client_metadata", OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);
|
||||
if (!isValidRedirectUris(
|
||||
clientRegistrationAuthentication.getClientRegistration().getPostLogoutRedirectUris())) {
|
||||
throwInvalidClientRegistration("invalid_client_metadata",
|
||||
OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS);
|
||||
}
|
||||
|
||||
if (!isValidTokenEndpointAuthenticationMethod(clientRegistrationAuthentication.getClientRegistration())) {
|
||||
throwInvalidClientRegistration("invalid_client_metadata", OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);
|
||||
throwInvalidClientRegistration("invalid_client_metadata",
|
||||
OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD);
|
||||
}
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Validated client registration request parameters");
|
||||
}
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientConverter.convert(clientRegistrationAuthentication.getClientRegistration());
|
||||
RegisteredClient registeredClient = this.registeredClientConverter
|
||||
.convert(clientRegistrationAuthentication.getClientRegistration());
|
||||
|
||||
if (StringUtils.hasText(registeredClient.getClientSecret())) {
|
||||
// Encode the client secret
|
||||
RegisteredClient updatedRegisteredClient = RegisteredClient.from(registeredClient)
|
||||
.clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret()))
|
||||
.build();
|
||||
.clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret()))
|
||||
.build();
|
||||
this.registeredClientRepository.save(updatedRegisteredClient);
|
||||
if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistrationAuthentication.getClientRegistration().getTokenEndpointAuthenticationMethod())) {
|
||||
if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()
|
||||
.equals(clientRegistrationAuthentication.getClientRegistration()
|
||||
.getTokenEndpointAuthenticationMethod())) {
|
||||
// gh-1344 Return the hashed client_secret
|
||||
registeredClient = updatedRegisteredClient;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.registeredClientRepository.save(registeredClient);
|
||||
}
|
||||
|
||||
@@ -232,9 +259,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
OAuth2Authorization registeredClientAuthorization = registerAccessToken(registeredClient);
|
||||
|
||||
// Invalidate the "initial" access token as it can only be used once
|
||||
authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getAccessToken().getToken());
|
||||
authorization = OidcAuthenticationProviderUtils.invalidate(authorization,
|
||||
authorization.getAccessToken().getToken());
|
||||
if (authorization.getRefreshToken() != null) {
|
||||
authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getRefreshToken().getToken());
|
||||
authorization = OidcAuthenticationProviderUtils.invalidate(authorization,
|
||||
authorization.getRefreshToken().getToken());
|
||||
}
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
@@ -242,10 +271,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
this.logger.trace("Saved authorization with invalidated initial access token");
|
||||
}
|
||||
|
||||
Map<String, Object> clientRegistrationClaims = this.clientRegistrationConverter.convert(registeredClient).getClaims();
|
||||
Map<String, Object> clientRegistrationClaims = this.clientRegistrationConverter.convert(registeredClient)
|
||||
.getClaims();
|
||||
OidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(clientRegistrationClaims)
|
||||
.registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue())
|
||||
.build();
|
||||
.registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue())
|
||||
.build();
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Authenticated client registration request");
|
||||
@@ -257,10 +287,12 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
|
||||
private OAuth2Authorization registerAccessToken(RegisteredClient registeredClient) {
|
||||
OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient,
|
||||
registeredClient.getClientAuthenticationMethods().iterator().next(), registeredClient.getClientSecret());
|
||||
registeredClient.getClientAuthenticationMethods().iterator().next(),
|
||||
registeredClient.getClientSecret());
|
||||
|
||||
Set<String> authorizedScopes = new HashSet<>();
|
||||
authorizedScopes.add(OidcClientConfigurationAuthenticationProvider.DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE);
|
||||
authorizedScopes
|
||||
.add(OidcClientConfigurationAuthenticationProvider.DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE);
|
||||
authorizedScopes = Collections.unmodifiableSet(authorizedScopes);
|
||||
|
||||
// @formatter:off
|
||||
@@ -296,9 +328,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
.authorizedScopes(authorizedScopes);
|
||||
// @formatter:on
|
||||
if (registrationAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.token(accessToken, (metadata) ->
|
||||
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) registrationAccessToken).getClaims()));
|
||||
} else {
|
||||
authorizationBuilder.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) registrationAccessToken).getClaims()));
|
||||
}
|
||||
else {
|
||||
authorizationBuilder.accessToken(accessToken);
|
||||
}
|
||||
|
||||
@@ -314,14 +348,16 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken, Set<String> requiredScope) {
|
||||
private static void checkScope(OAuth2Authorization.Token<OAuth2AccessToken> authorizedAccessToken,
|
||||
Set<String> requiredScope) {
|
||||
Collection<String> authorizedScope = Collections.emptySet();
|
||||
if (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) {
|
||||
authorizedScope = (Collection<String>) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE);
|
||||
}
|
||||
if (!authorizedScope.containsAll(requiredScope)) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE);
|
||||
} else if (authorizedScope.size() != requiredScope.size()) {
|
||||
}
|
||||
else if (authorizedScope.size() != requiredScope.size()) {
|
||||
// Restrict the access token to only contain the required scope
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
@@ -338,7 +374,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
if (validRedirectUri.getFragment() != null) {
|
||||
return false;
|
||||
}
|
||||
} catch (URISyntaxException ex) {
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -350,8 +387,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
String authenticationMethod = clientRegistration.getTokenEndpointAuthenticationMethod();
|
||||
String authenticationSigningAlgorithm = clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm();
|
||||
|
||||
if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod) &&
|
||||
!ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(authenticationMethod)) {
|
||||
if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod)
|
||||
&& !ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(authenticationMethod)) {
|
||||
return !StringUtils.hasText(authenticationSigningAlgorithm);
|
||||
}
|
||||
|
||||
@@ -360,21 +397,18 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe
|
||||
}
|
||||
|
||||
if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod)) {
|
||||
return clientRegistration.getJwkSetUrl() != null &&
|
||||
(!StringUtils.hasText(authenticationSigningAlgorithm) ||
|
||||
SignatureAlgorithm.from(authenticationSigningAlgorithm) != null);
|
||||
} else {
|
||||
return clientRegistration.getJwkSetUrl() != null && (!StringUtils.hasText(authenticationSigningAlgorithm)
|
||||
|| SignatureAlgorithm.from(authenticationSigningAlgorithm) != null);
|
||||
}
|
||||
else {
|
||||
// client_secret_jwt
|
||||
return !StringUtils.hasText(authenticationSigningAlgorithm) ||
|
||||
MacAlgorithm.from(authenticationSigningAlgorithm) != null;
|
||||
return !StringUtils.hasText(authenticationSigningAlgorithm)
|
||||
|| MacAlgorithm.from(authenticationSigningAlgorithm) != null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwInvalidClientRegistration(String errorCode, String fieldName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
errorCode,
|
||||
"Invalid Client Registration: " + fieldName,
|
||||
ERROR_URI);
|
||||
OAuth2Error error = new OAuth2Error(errorCode, "Invalid Client Registration: " + fieldName, ERROR_URI);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client Registration (and Configuration) Endpoint.
|
||||
* An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client
|
||||
* Registration (and Configuration) Endpoint.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @author Ovidiu Popa
|
||||
@@ -36,18 +37,23 @@ import org.springframework.util.Assert;
|
||||
* @see OidcClientConfigurationAuthenticationProvider
|
||||
*/
|
||||
public class OidcClientRegistrationAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final OidcClientRegistration clientRegistration;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param principal the authenticated principal
|
||||
* @param clientRegistration the client registration
|
||||
*/
|
||||
public OidcClientRegistrationAuthenticationToken(Authentication principal, OidcClientRegistration clientRegistration) {
|
||||
public OidcClientRegistrationAuthenticationToken(Authentication principal,
|
||||
OidcClientRegistration clientRegistration) {
|
||||
super(Collections.emptyList());
|
||||
Assert.notNull(principal, "principal cannot be null");
|
||||
Assert.notNull(clientRegistration, "clientRegistration cannot be null");
|
||||
@@ -58,8 +64,8 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param principal the authenticated principal
|
||||
* @param clientId the client identifier
|
||||
* @since 0.2.1
|
||||
@@ -86,7 +92,6 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
|
||||
|
||||
/**
|
||||
* Returns the client registration.
|
||||
*
|
||||
* @return the client registration
|
||||
*/
|
||||
public OidcClientRegistration getClientRegistration() {
|
||||
@@ -95,7 +100,6 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic
|
||||
|
||||
/**
|
||||
* Returns the client identifier.
|
||||
*
|
||||
* @return the client identifier
|
||||
* @since 0.2.1
|
||||
*/
|
||||
|
||||
@@ -47,29 +47,36 @@ import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 RP-Initiated Logout Endpoint.
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 RP-Initiated
|
||||
* Logout Endpoint.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.1
|
||||
* @see RegisteredClientRepository
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see SessionRegistry
|
||||
* @see <a href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout">2. RP-Initiated Logout</a>
|
||||
* @see <a href="https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout">2.
|
||||
* RP-Initiated Logout</a>
|
||||
*/
|
||||
public final class OidcLogoutAuthenticationProvider implements AuthenticationProvider {
|
||||
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE =
|
||||
new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||
|
||||
private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN);
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final RegisteredClientRepository registeredClientRepository;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final SessionRegistry sessionRegistry;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcLogoutAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcLogoutAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param registeredClientRepository the repository of registered clients
|
||||
* @param authorizationService the authorization service
|
||||
* @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect sessions
|
||||
* @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect
|
||||
* sessions
|
||||
*/
|
||||
public OidcLogoutAuthenticationProvider(RegisteredClientRepository registeredClientRepository,
|
||||
OAuth2AuthorizationService authorizationService, SessionRegistry sessionRegistry) {
|
||||
@@ -83,11 +90,10 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OidcLogoutAuthenticationToken oidcLogoutAuthentication =
|
||||
(OidcLogoutAuthenticationToken) authentication;
|
||||
OidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication;
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
oidcLogoutAuthentication.getIdTokenHint(), ID_TOKEN_TOKEN_TYPE);
|
||||
OAuth2Authorization authorization = this.authorizationService
|
||||
.findByToken(oidcLogoutAuthentication.getIdTokenHint(), ID_TOKEN_TOKEN_TYPE);
|
||||
if (authorization == null) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_TOKEN, "id_token_hint");
|
||||
}
|
||||
@@ -97,13 +103,13 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
}
|
||||
|
||||
OAuth2Authorization.Token<OidcIdToken> authorizedIdToken = authorization.getToken(OidcIdToken.class);
|
||||
if (authorizedIdToken.isInvalidated() ||
|
||||
authorizedIdToken.isBeforeUse()) { // Expired ID Token should be accepted
|
||||
if (authorizedIdToken.isInvalidated() || authorizedIdToken.isBeforeUse()) {
|
||||
// Expired ID Token should be accepted
|
||||
throwError(OAuth2ErrorCodes.INVALID_TOKEN, "id_token_hint");
|
||||
}
|
||||
|
||||
RegisteredClient registeredClient = this.registeredClientRepository.findById(
|
||||
authorization.getRegisteredClientId());
|
||||
RegisteredClient registeredClient = this.registeredClientRepository
|
||||
.findById(authorization.getRegisteredClientId());
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace("Retrieved registered client");
|
||||
@@ -113,16 +119,16 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
|
||||
// Validate client identity
|
||||
List<String> audClaim = idToken.getAudience();
|
||||
if (CollectionUtils.isEmpty(audClaim) ||
|
||||
!audClaim.contains(registeredClient.getClientId())) {
|
||||
if (CollectionUtils.isEmpty(audClaim) || !audClaim.contains(registeredClient.getClientId())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.AUD);
|
||||
}
|
||||
if (StringUtils.hasText(oidcLogoutAuthentication.getClientId()) &&
|
||||
!oidcLogoutAuthentication.getClientId().equals(registeredClient.getClientId())) {
|
||||
if (StringUtils.hasText(oidcLogoutAuthentication.getClientId())
|
||||
&& !oidcLogoutAuthentication.getClientId().equals(registeredClient.getClientId())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);
|
||||
}
|
||||
if (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri()) &&
|
||||
!registeredClient.getPostLogoutRedirectUris().contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {
|
||||
if (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())
|
||||
&& !registeredClient.getPostLogoutRedirectUris()
|
||||
.contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_REQUEST, "post_logout_redirect_uri");
|
||||
}
|
||||
|
||||
@@ -134,28 +140,28 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
if (oidcLogoutAuthentication.isPrincipalAuthenticated()) {
|
||||
Authentication currentUserPrincipal = (Authentication) oidcLogoutAuthentication.getPrincipal();
|
||||
Authentication authorizedUserPrincipal = authorization.getAttribute(Principal.class.getName());
|
||||
if (!StringUtils.hasText(idToken.getSubject()) ||
|
||||
!currentUserPrincipal.getName().equals(authorizedUserPrincipal.getName())) {
|
||||
if (!StringUtils.hasText(idToken.getSubject())
|
||||
|| !currentUserPrincipal.getName().equals(authorizedUserPrincipal.getName())) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.SUB);
|
||||
}
|
||||
|
||||
// Check for active session
|
||||
if (StringUtils.hasText(oidcLogoutAuthentication.getSessionId())) {
|
||||
SessionInformation sessionInformation = findSessionInformation(
|
||||
currentUserPrincipal, oidcLogoutAuthentication.getSessionId());
|
||||
SessionInformation sessionInformation = findSessionInformation(currentUserPrincipal,
|
||||
oidcLogoutAuthentication.getSessionId());
|
||||
if (sessionInformation != null) {
|
||||
String sessionIdHash;
|
||||
try {
|
||||
sessionIdHash = createHash(sessionInformation.getSessionId());
|
||||
} catch (NoSuchAlgorithmException ex) {
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
|
||||
"Failed to compute hash for Session ID.", null);
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
|
||||
String sidClaim = idToken.getClaim("sid");
|
||||
if (!StringUtils.hasText(sidClaim) ||
|
||||
!sidClaim.equals(sessionIdHash)) {
|
||||
if (!StringUtils.hasText(sidClaim) || !sidClaim.equals(sessionIdHash)) {
|
||||
throwError(OAuth2ErrorCodes.INVALID_TOKEN, "sid");
|
||||
}
|
||||
}
|
||||
@@ -191,9 +197,7 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
}
|
||||
|
||||
private static void throwError(String errorCode, String parameterName) {
|
||||
OAuth2Error error = new OAuth2Error(
|
||||
errorCode,
|
||||
"OpenID Connect 1.0 Logout Request Parameter: " + parameterName,
|
||||
OAuth2Error error = new OAuth2Error(errorCode, "OpenID Connect 1.0 Logout Request Parameter: " + parameterName,
|
||||
"https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling");
|
||||
throw new OAuth2AuthenticationException(error);
|
||||
}
|
||||
@@ -203,4 +207,5 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro
|
||||
byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));
|
||||
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link Authentication} implementation used for OpenID Connect 1.0 RP-Initiated Logout Endpoint.
|
||||
* An {@link Authentication} implementation used for OpenID Connect 1.0 RP-Initiated
|
||||
* Logout Endpoint.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 1.1
|
||||
@@ -34,24 +35,35 @@ import org.springframework.util.Assert;
|
||||
* @see OidcLogoutAuthenticationProvider
|
||||
*/
|
||||
public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final String idTokenHint;
|
||||
|
||||
private final OidcIdToken idToken;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final String sessionId;
|
||||
|
||||
private final String clientId;
|
||||
|
||||
private final String postLogoutRedirectUri;
|
||||
|
||||
private final String state;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* @param idTokenHint the ID Token previously issued by the Provider to the Client and used as a hint about the End-User's current authenticated session with the Client
|
||||
* @param idTokenHint the ID Token previously issued by the Provider to the Client and
|
||||
* used as a hint about the End-User's current authenticated session with the Client
|
||||
* @param principal the authenticated principal representing the End-User
|
||||
* @param sessionId the End-User's current authenticated session identifier with the Provider
|
||||
* @param sessionId the End-User's current authenticated session identifier with the
|
||||
* Provider
|
||||
* @param clientId the client identifier the ID Token was issued to
|
||||
* @param postLogoutRedirectUri the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed
|
||||
* @param state the opaque value used by the Client to maintain state between the logout request and the callback to the {@code postLogoutRedirectUri}
|
||||
* @param postLogoutRedirectUri the URI which the Client is requesting that the
|
||||
* End-User's User Agent be redirected to after a logout has been performed
|
||||
* @param state the opaque value used by the Client to maintain state between the
|
||||
* logout request and the callback to the {@code postLogoutRedirectUri}
|
||||
*/
|
||||
public OidcLogoutAuthenticationToken(String idTokenHint, Authentication principal, @Nullable String sessionId,
|
||||
@Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) {
|
||||
@@ -70,13 +82,15 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* @param idToken the ID Token previously issued by the Provider to the Client
|
||||
* @param principal the authenticated principal representing the End-User
|
||||
* @param sessionId the End-User's current authenticated session identifier with the Provider
|
||||
* @param sessionId the End-User's current authenticated session identifier with the
|
||||
* Provider
|
||||
* @param clientId the client identifier the ID Token was issued to
|
||||
* @param postLogoutRedirectUri the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed
|
||||
* @param state the opaque value used by the Client to maintain state between the logout request and the callback to the {@code postLogoutRedirectUri}
|
||||
* @param postLogoutRedirectUri the URI which the Client is requesting that the
|
||||
* End-User's User Agent be redirected to after a logout has been performed
|
||||
* @param state the opaque value used by the Client to maintain state between the
|
||||
* logout request and the callback to the {@code postLogoutRedirectUri}
|
||||
*/
|
||||
public OidcLogoutAuthenticationToken(OidcIdToken idToken, Authentication principal, @Nullable String sessionId,
|
||||
@Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) {
|
||||
@@ -95,7 +109,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
/**
|
||||
* Returns the authenticated principal representing the End-User.
|
||||
*
|
||||
* @return the authenticated principal representing the End-User
|
||||
*/
|
||||
@Override
|
||||
@@ -104,13 +117,14 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise.
|
||||
*
|
||||
* @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise
|
||||
* Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false}
|
||||
* otherwise.
|
||||
* @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false}
|
||||
* otherwise
|
||||
*/
|
||||
public boolean isPrincipalAuthenticated() {
|
||||
return !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass()) &&
|
||||
this.principal.isAuthenticated();
|
||||
return !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass())
|
||||
&& this.principal.isAuthenticated();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,9 +133,8 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID Token previously issued by the Provider to the Client and used as a hint
|
||||
* about the End-User's current authenticated session with the Client.
|
||||
*
|
||||
* Returns the ID Token previously issued by the Provider to the Client and used as a
|
||||
* hint about the End-User's current authenticated session with the Client.
|
||||
* @return the ID Token previously issued by the Provider to the Client
|
||||
*/
|
||||
public String getIdTokenHint() {
|
||||
@@ -130,7 +143,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
/**
|
||||
* Returns the ID Token previously issued by the Provider to the Client.
|
||||
*
|
||||
* @return the ID Token previously issued by the Provider to the Client
|
||||
*/
|
||||
@Nullable
|
||||
@@ -140,7 +152,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
/**
|
||||
* Returns the End-User's current authenticated session identifier with the Provider.
|
||||
*
|
||||
* @return the End-User's current authenticated session identifier with the Provider
|
||||
*/
|
||||
@Nullable
|
||||
@@ -150,7 +161,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
/**
|
||||
* Returns the client identifier the ID Token was issued to.
|
||||
*
|
||||
* @return the client identifier
|
||||
*/
|
||||
@Nullable
|
||||
@@ -159,9 +169,10 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed.
|
||||
*
|
||||
* @return the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed
|
||||
* Returns the URI which the Client is requesting that the End-User's User Agent be
|
||||
* redirected to after a logout has been performed.
|
||||
* @return the URI which the Client is requesting that the End-User's User Agent be
|
||||
* redirected to after a logout has been performed
|
||||
*/
|
||||
@Nullable
|
||||
public String getPostLogoutRedirectUri() {
|
||||
@@ -169,9 +180,10 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the opaque value used by the Client to maintain state between the logout request and the callback to the {@link #getPostLogoutRedirectUri()}.
|
||||
*
|
||||
* @return the opaque value used by the Client to maintain state between the logout request and the callback to the {@link #getPostLogoutRedirectUri()}
|
||||
* Returns the opaque value used by the Client to maintain state between the logout
|
||||
* request and the callback to the {@link #getPostLogoutRedirectUri()}.
|
||||
* @return the opaque value used by the Client to maintain state between the logout
|
||||
* request and the callback to the {@link #getPostLogoutRedirectUri()}
|
||||
*/
|
||||
@Nullable
|
||||
public String getState() {
|
||||
|
||||
@@ -28,8 +28,9 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link OAuth2AuthenticationContext} that holds an {@link OidcUserInfoAuthenticationToken} and additional information
|
||||
* and is used when mapping claims to an instance of {@link OidcUserInfo}.
|
||||
* An {@link OAuth2AuthenticationContext} that holds an
|
||||
* {@link OidcUserInfoAuthenticationToken} and additional information and is used when
|
||||
* mapping claims to an instance of {@link OidcUserInfo}.
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 0.2.1
|
||||
@@ -38,6 +39,7 @@ import org.springframework.util.Assert;
|
||||
* @see OidcUserInfoAuthenticationProvider#setUserInfoMapper(Function)
|
||||
*/
|
||||
public final class OidcUserInfoAuthenticationContext implements OAuth2AuthenticationContext {
|
||||
|
||||
private final Map<Object, Object> context;
|
||||
|
||||
private OidcUserInfoAuthenticationContext(Map<Object, Object> context) {
|
||||
@@ -59,7 +61,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
|
||||
*
|
||||
* @return the {@link OAuth2AccessToken}
|
||||
*/
|
||||
public OAuth2AccessToken getAccessToken() {
|
||||
@@ -68,7 +69,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
|
||||
/**
|
||||
* Returns the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @return the {@link OAuth2Authorization}
|
||||
*/
|
||||
public OAuth2Authorization getAuthorization() {
|
||||
@@ -76,8 +76,8 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Builder} with the provided {@link OidcUserInfoAuthenticationToken}.
|
||||
*
|
||||
* Constructs a new {@link Builder} with the provided
|
||||
* {@link OidcUserInfoAuthenticationToken}.
|
||||
* @param authentication the {@link OidcUserInfoAuthenticationToken}
|
||||
* @return the {@link Builder}
|
||||
*/
|
||||
@@ -96,7 +96,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2AccessToken OAuth 2.0 Access Token}.
|
||||
*
|
||||
* @param accessToken the {@link OAuth2AccessToken}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -106,7 +105,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
|
||||
/**
|
||||
* Sets the {@link OAuth2Authorization authorization}.
|
||||
*
|
||||
* @param authorization the {@link OAuth2Authorization}
|
||||
* @return the {@link Builder} for further configuration
|
||||
*/
|
||||
@@ -116,7 +114,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica
|
||||
|
||||
/**
|
||||
* Builds a new {@link OidcUserInfoAuthenticationContext}.
|
||||
*
|
||||
* @return the {@link OidcUserInfoAuthenticationContext}
|
||||
*/
|
||||
public OidcUserInfoAuthenticationContext build() {
|
||||
|
||||
@@ -43,21 +43,26 @@ import org.springframework.security.oauth2.server.resource.authentication.Abstra
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 UserInfo Endpoint.
|
||||
* An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 UserInfo
|
||||
* Endpoint.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @since 0.2.1
|
||||
* @see OAuth2AuthorizationService
|
||||
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfo">5.3. UserInfo Endpoint</a>
|
||||
* @see <a href="https://openid.net/specs/openid-connect-core-1_0.html#UserInfo">5.3.
|
||||
* UserInfo Endpoint</a>
|
||||
*/
|
||||
public final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = new DefaultOidcUserInfoMapper();
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided
|
||||
* parameters.
|
||||
* @param authorizationService the authorization service
|
||||
*/
|
||||
public OidcUserInfoAuthenticationProvider(OAuth2AuthorizationService authorizationService) {
|
||||
@@ -67,12 +72,13 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
OidcUserInfoAuthenticationToken userInfoAuthentication =
|
||||
(OidcUserInfoAuthenticationToken) authentication;
|
||||
OidcUserInfoAuthenticationToken userInfoAuthentication = (OidcUserInfoAuthenticationToken) authentication;
|
||||
|
||||
AbstractOAuth2TokenAuthenticationToken<?> accessTokenAuthentication = null;
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(userInfoAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) userInfoAuthentication.getPrincipal();
|
||||
if (AbstractOAuth2TokenAuthenticationToken.class
|
||||
.isAssignableFrom(userInfoAuthentication.getPrincipal().getClass())) {
|
||||
accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken<?>) userInfoAuthentication
|
||||
.getPrincipal();
|
||||
}
|
||||
if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
@@ -80,8 +86,8 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
|
||||
String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue();
|
||||
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(
|
||||
accessTokenValue, OAuth2TokenType.ACCESS_TOKEN);
|
||||
OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue,
|
||||
OAuth2TokenType.ACCESS_TOKEN);
|
||||
if (authorization == null) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
}
|
||||
@@ -108,11 +114,11 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
this.logger.trace("Validated user info request");
|
||||
}
|
||||
|
||||
OidcUserInfoAuthenticationContext authenticationContext =
|
||||
OidcUserInfoAuthenticationContext.with(userInfoAuthentication)
|
||||
.accessToken(authorizedAccessToken.getToken())
|
||||
.authorization(authorization)
|
||||
.build();
|
||||
OidcUserInfoAuthenticationContext authenticationContext = OidcUserInfoAuthenticationContext
|
||||
.with(userInfoAuthentication)
|
||||
.accessToken(authorizedAccessToken.getToken())
|
||||
.authorization(authorization)
|
||||
.build();
|
||||
OidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext);
|
||||
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
@@ -128,27 +134,33 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext}
|
||||
* to an instance of {@link OidcUserInfo} for the UserInfo response.
|
||||
* Sets the {@link Function} used to extract claims from
|
||||
* {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* for the UserInfo response.
|
||||
*
|
||||
* <p>
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken},
|
||||
* as well as, the following context attributes:
|
||||
* The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the
|
||||
* {@link OidcUserInfoAuthenticationToken}, as well as, the following context
|
||||
* attributes:
|
||||
* <ul>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and
|
||||
* {@link OAuth2AccessToken} associated with the bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the
|
||||
* bearer token used to make the request.</li>
|
||||
* <li>{@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the
|
||||
* {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token
|
||||
* used to make the request.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
* @param userInfoMapper the {@link Function} used to extract claims from
|
||||
* {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo}
|
||||
*/
|
||||
public void setUserInfoMapper(Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper) {
|
||||
Assert.notNull(userInfoMapper, "userInfoMapper cannot be null");
|
||||
this.userInfoMapper = userInfoMapper;
|
||||
}
|
||||
|
||||
private static final class DefaultOidcUserInfoMapper implements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {
|
||||
private static final class DefaultOidcUserInfoMapper
|
||||
implements Function<OidcUserInfoAuthenticationContext, OidcUserInfo> {
|
||||
|
||||
// @formatter:off
|
||||
private static final List<String> EMAIL_CLAIMS = Arrays.asList(
|
||||
StandardClaimNames.EMAIL,
|
||||
StandardClaimNames.EMAIL_VERIFIED
|
||||
@@ -173,6 +185,7 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
StandardClaimNames.LOCALE,
|
||||
StandardClaimNames.UPDATED_AT
|
||||
);
|
||||
// @formatter:on
|
||||
|
||||
@Override
|
||||
public OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) {
|
||||
@@ -185,7 +198,8 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP
|
||||
return new OidcUserInfo(scopeRequestedClaims);
|
||||
}
|
||||
|
||||
private static Map<String, Object> getClaimsRequestedByScope(Map<String, Object> claims, Set<String> requestedScopes) {
|
||||
private static Map<String, Object> getClaimsRequestedByScope(Map<String, Object> claims,
|
||||
Set<String> requestedScopes) {
|
||||
Set<String> scopeRequestedClaimNames = new HashSet<>(32);
|
||||
scopeRequestedClaimNames.add(StandardClaimNames.SUB);
|
||||
|
||||
|
||||
@@ -33,13 +33,16 @@ import org.springframework.util.Assert;
|
||||
* @see OidcUserInfoAuthenticationProvider
|
||||
*/
|
||||
public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Authentication principal;
|
||||
|
||||
private final OidcUserInfo userInfo;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcUserInfoAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcUserInfoAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param principal the principal
|
||||
*/
|
||||
public OidcUserInfoAuthenticationToken(Authentication principal) {
|
||||
@@ -51,8 +54,8 @@ public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an {@code OidcUserInfoAuthenticationToken} using the provided parameters.
|
||||
*
|
||||
* Constructs an {@code OidcUserInfoAuthenticationToken} using the provided
|
||||
* parameters.
|
||||
* @param principal the authenticated principal
|
||||
* @param userInfo the UserInfo claims
|
||||
*/
|
||||
@@ -77,7 +80,6 @@ public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken
|
||||
|
||||
/**
|
||||
* Returns the UserInfo claims.
|
||||
*
|
||||
* @return the UserInfo claims
|
||||
*/
|
||||
public OidcUserInfo getUserInfo() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user