Merge branch '1.1.x' into 1.2.x

This commit is contained in:
Joe Grandja
2024-05-16 05:56:09 -04:00
257 changed files with 11231 additions and 9739 deletions

View File

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

View File

@@ -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";

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

@@ -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";

View File

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

View File

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

View File

@@ -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();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();

View File

@@ -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();
}
}

View File

@@ -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) {

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

@@ -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())) {

View File

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

View File

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

View File

@@ -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() {

View File

@@ -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();
}

View File

@@ -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() {

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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() {

View File

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

View File

@@ -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() {

View File

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

View File

@@ -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() {

View File

@@ -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();
}
}

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

@@ -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());
}
});
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<>();

View File

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

View File

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

View File

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

View File

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

View File

@@ -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));

View File

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

View File

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

View File

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

View File

@@ -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();

View File

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

View File

@@ -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);

View File

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

View File

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

View File

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

View File

@@ -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();
}

View File

@@ -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() {

View File

@@ -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";

View File

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

View File

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

View File

@@ -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
*/

View File

@@ -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";

View File

@@ -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();
}
}

View File

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

View File

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

View File

@@ -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
*/

View File

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

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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);

View File

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