Skip to content

Implement OAuth 2.0 Server Metadata (RFC 8414) #167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerConfigurationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter;
Expand Down Expand Up @@ -89,13 +90,15 @@ public final class OAuth2AuthorizationServerConfigurer<B extends HttpSecurityBui
private RequestMatcher tokenRevocationEndpointMatcher;
private RequestMatcher jwkSetEndpointMatcher;
private RequestMatcher oidcProviderConfigurationEndpointMatcher;
private RequestMatcher oauth2ServerConfigurationEndpointMatcher;
private final RequestMatcher endpointsMatcher = (request) ->
this.authorizationEndpointMatcher.matches(request) ||
this.tokenEndpointMatcher.matches(request) ||
this.tokenIntrospectionEndpointMatcher.matches(request) ||
this.tokenRevocationEndpointMatcher.matches(request) ||
this.jwkSetEndpointMatcher.matches(request) ||
this.oidcProviderConfigurationEndpointMatcher.matches(request);
this.oidcProviderConfigurationEndpointMatcher.matches(request) ||
this.oauth2ServerConfigurationEndpointMatcher.matches(request);

/**
* Sets the repository of registered clients.
Expand Down Expand Up @@ -214,6 +217,10 @@ public void configure(B builder) {
OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter =
new OidcProviderConfigurationEndpointFilter(providerSettings);
builder.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);

OAuth2AuthorizationServerConfigurationEndpointFilter authorizationServerConfigurationFilter
= new OAuth2AuthorizationServerConfigurationEndpointFilter(providerSettings);
builder.addFilterBefore(postProcess(authorizationServerConfigurationFilter), AbstractPreAuthenticatedProcessingFilter.class);
}

JWKSource<SecurityContext> jwkSource = getJwkSource(builder);
Expand Down Expand Up @@ -277,6 +284,8 @@ private void initEndpointMatchers(ProviderSettings providerSettings) {
providerSettings.jwkSetEndpoint(), HttpMethod.GET.name());
this.oidcProviderConfigurationEndpointMatcher = new AntPathRequestMatcher(
OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name());
this.oauth2ServerConfigurationEndpointMatcher = new AntPathRequestMatcher(
OAuth2AuthorizationServerConfigurationEndpointFilter.DEFAULT_OAUTH2_AUTHORIZATION_SERVER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name());
}

private static void validateProviderSettings(ProviderSettings providerSettings) {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;


import java.net.URL;
import java.util.List;

/**
* A base {@link ClaimAccessor} for the "claims" the Authorization Server can make about
* its configuration, used either in OpenID Connect Discovery 1.0 or OAuth 2.0 Authorization
* Server Metadata.
*
* @author Daniel Garnier-Moiroux
* @since 0.1.1
* @see ClaimAccessor
* @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>
*/
public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor {

/**
* 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() {
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.ISSUER);
}

/**
* 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() {
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT);
}

/**
* 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() {
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT);
}

/**
* 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() {
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED);
}

/**
* 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 getJwkSetUri() {
return getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI);
}

/**
* 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() {
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_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() {
return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED);
}

/**
* 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() {
return this.getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED);
}

/**
* 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() {
return this.getClaimAsURL(OAuth2AuthorizationServerMetadataClaimNames.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 this.getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED);
}

/**
* Returns the Proof Key for Code Exchange (PKCE) code challenge methods supported by the
* OAuth 2.0 Authorization Server {@code (code_challenge_methods_supported)}.
*
* @return the code challenge methods supported by the OAuth 2.0 Authorization Server
*/
default List<String> getCodeChallengeMethods() {
return this.getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core;

import org.springframework.security.oauth2.core.oidc.OidcProviderMetadataClaimNames;

/**
* The names of the "claims" an Authorization Server can make about its configuration,
* used either in OpenID Connect Discovery 1.0 or OAuth 2.0 Authorization Server Metadata.
*
* @author Daniel Garnier-Moiroux
* @since 0.1.1
* @see OidcProviderMetadataClaimNames
* @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>
*/
public interface OAuth2AuthorizationServerMetadataClaimNames {

/**
* {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer Identifier
*/
String ISSUER = "issuer";

/**
* {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization Endpoint
*/
String AUTHORIZATION_ENDPOINT = "authorization_endpoint";

/**
* {@code token_endpoint} - the {@code URL} of the OAuth 2.0 Token Endpoint
*/
String TOKEN_ENDPOINT = "token_endpoint";

/**
* {@code token_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Endpoint
*/
String TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED = "token_endpoint_auth_methods_supported";

/**
* {@code jwks_uri} - the {@code URL} of the JSON Web Key Set
*/
String JWKS_URI = "jwks_uri";

/**
* {@code response_types_supported} - the OAuth 2.0 {@code response_type} values supported
*/
String RESPONSE_TYPES_SUPPORTED = "response_types_supported";

/**
* {@code grant_types_supported} - the OAuth 2.0 {@code grant_type} values supported
*/
String GRANT_TYPES_SUPPORTED = "grant_types_supported";

/**
* {@code scopes_supported} - the OAuth 2.0 {@code scope} values supported
*/
String SCOPES_SUPPORTED = "scopes_supported";

/**
* {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation Endpoint
*/
String REVOCATION_ENDPOINT = "revocation_endpoint";

/**
* {@code token_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint
*/
String REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED = "revocation_endpoint_auth_methods_supported";

/**
* {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE) code challenge methods
* supported by the OAuth 2.0 Authorization Server
*/
String CODE_CHALLENGE_METHODS_SUPPORTED = "code_challenge_methods_supported";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.core.endpoint;

import org.springframework.security.oauth2.core.AbstractOAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.core.OAuth2AuthorizationServerMetadataClaimAccessor;
import org.springframework.security.oauth2.core.Version;
import org.springframework.util.Assert;

import java.io.Serializable;
import java.util.Map;

/**
* A representation of an OAuth 2.0 Authorization Server Configuration response,
* which is returned form an OAuth 2.0 Authorization Server's Configuration 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 AbstractOAuth2AuthorizationServerConfiguration
* @see OAuth2AuthorizationServerMetadataClaimAccessor
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc8414#section-3.2">3.2. Authorization Server Metadata Response</a>
*/
public final class OAuth2AuthorizationServerConfiguration extends AbstractOAuth2AuthorizationServerConfiguration
implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable {
private static final long serialVersionUID = Version.SERIAL_VERSION_UID;

private OAuth2AuthorizationServerConfiguration(Map<String, Object> claims) {
super(claims);
}

/**
* Constructs a new {@link Builder} with empty claims.
*
* @return the {@link Builder}
*/
public static Builder builder() {
return new Builder();
}

/**
* 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));
}

/**
* Helps configure an {@link OAuth2AuthorizationServerConfiguration}.
*/
public static class Builder
extends AbstractOAuth2AuthorizationServerConfiguration.AbstractBuilder<OAuth2AuthorizationServerConfiguration, Builder> {
private Builder() {
}

/**
* Validate the claims and build the {@link OAuth2AuthorizationServerConfiguration}.
* <p>
* The following claims are REQUIRED:
* {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint},
* {@code jwks_uri} and {@code response_types_supported}.
*
* @return the {@link OAuth2AuthorizationServerConfiguration}
*/
public OAuth2AuthorizationServerConfiguration build() {
validateCommonClaims();
removeEmptyClaims();
return new OAuth2AuthorizationServerConfiguration(this.claims);
}

}
}
Loading