Skip to content

Add ClientRegistration from OpenID Connect Discovery #5355

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

Conversation

rwinch
Copy link
Member

@rwinch rwinch commented May 16, 2018

With this user's can now create a ClientRegistration using something like this:

@Bean
ReactiveClientRegistrationRepository clientRegistrationRepository() {
	ClientRegistration registration = OidcConfigurationProvider.issuer("https://accounts.google.com")
			.clientId("client-id")
			.clientSecret("client-secret")
			.build();
	return new InMemoryReactiveClientRegistrationRepository(registration);
}

Fixes: gh-4413

@rwinch rwinch added New Feature in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) labels May 16, 2018
@rwinch rwinch added this to the 5.1.0.M2 milestone May 16, 2018
@rwinch rwinch requested a review from jgrandja May 16, 2018 17:35
@sparty02
Copy link

sparty02 commented May 17, 2018

What about defining the OIDC discovery config in application.yaml (as described in #5155)?

@rwinch
Copy link
Member Author

rwinch commented May 17, 2018

@sparty02 Thanks for your feedback!

Making it simple to create the Bean definition will be a feature added to the Spring Boot AutoConfiguration support for Spring Security once this gets merged in. This aligns with the way creating ClientRegistration works today. Spring Security provides an API to create the ClientRegistration and Spring Boot provides a configuration convention to convert configuration properties into a ClientRegistrationRepository using the provided APIs. This arrangement is necessary because only with Spring Boot can we conditionally create the Beans and back out of the way.

In the meantime you can always Externalize Configuration yourself.

I hope this arrangement makes sense. Please let me know if you have additional questions or concerns.

@sparty02
Copy link

@rwinch thanks, sounds good!

@jgrandja jgrandja added OIDC in: config An issue in spring-security-config labels May 18, 2018
@rwinch rwinch merged commit 0598d47 into spring-projects:master May 18, 2018
rwinch added a commit that referenced this pull request May 18, 2018
This reverts commit 9fe0f50.

The original commit was accidentally pushed prior to PR. We attempted
to revert the commit hoping the PR would open again. This did not work.
We are going to do a Polish commit instead.

Issue: gh-5355
* limitations under the License.
*/

package org.springframework.security.config.oauth2.client;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add to package ...oauth2.client.oidc

* Provider Configuration Response</a> to initialize the {@link ClientRegistration.Builder}.
*
* <p>
* For example if the issuer provided is "https://example.com", then an "OpenID Provider Configuration Request" will
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use "&quot ;" entity for all

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" do not need to be encoded and this is difficult for readability. I'm going to leave this as is

public final class OidcConfigurationProvider {

/**
* Given the <a href="http://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer</a> creates a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Creates a ClientRegistration.Builder using the provided Issuer by making an..."

* Provider Configuration Response</a> to initialize the {@link ClientRegistration.Builder}.
*
* <p>
* For example if the issuer provided is "https://example.com", then an "OpenID Provider Configuration Request" will
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"For example, if the..."

* @return a {@link ClientRegistration.Builder} that was initialized by the OpenID Provider Configuration.
*/
public static ClientRegistration.Builder issuer(String issuer) {
RestTemplate rest = new RestTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Assert.hasText(issuer)...also should we validate it's a URI?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think leaving this validation to the invocation of the RestTemplate is probably best.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not going to provide a user-friendly message compared to "Issuer cannot be empty/null" if we apply the Assert

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated this to provide an exception with the issuer in it if we fail to get a successful result back since valid URLs might not produce valid result.

* @return a {@link ClientRegistration.Builder} that was initialized by the OpenID Provider Configuration.
*/
public static ClientRegistration.Builder issuer(String issuer) {
RestTemplate rest = new RestTemplate();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use WebClient instead? Also, set as static member so we can re-use for each request?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RestTemplate has fewer dependencies, so I think sticking with that makes sense. Especially since starting the container is blocking anyways.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is not need timeout settings?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kazuki43zoo I think at this point we are going to leave it encapsulated. We probably need a separate issue to handle things like timeout settings more holistically. Likely this will be done by allowing the user to provide a WebClient or RestOperations instance

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to @kazuki43zoo's question, would RestTemplate better serve as an injected dependency? I recognize this changes the contract, since the method won't be static anymore, but just throwing it out there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jzheaux At some point. We need to decide more wholistically if this is going to be WebClient or RestOperations since this should be consistent throughout our APIs. For the moment, I think we should keep this private.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rwinch Thanks for your replay. I think keeping private is OK.
But I will afraid that it is possible become timeout of infinity in current implementation (new RestTemplate() = default implementation of theSimpleClientHttpRequestFactory ).

Scope scope = metadata.getScopes();
if (scope == null) {
// If null, default to "openid" which must be supported
return Arrays.asList("openid");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use OidcScopes.OPENID

* limitations under the License.
*/

package org.springframework.security.config.oauth2.client;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to sub-package ...oauth2.client.oidc

List<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> metadataAuthMethods = metadata.getTokenEndpointAuthMethods();
// if null, the default includes client_secret_basic
if (metadataAuthMethods != null && !metadataAuthMethods.contains(com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod.CLIENT_SECRET_BASIC)) {
throw new IllegalArgumentException("Only ClientAuthenticationMethod.BASIC is supported. The issuer \"" + issuer + "\" returned a configuration of " + metadataAuthMethods);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also support ClientAuthenticationMethod.POST

String openidConfiguration = rest.getForObject(issuer + "/.well-known/openid-configuration", String.class);
OIDCProviderMetadata metadata = parse(openidConfiguration);
String name = URI.create(issuer).getHost();
List<com.nimbusds.oauth2.sdk.auth.ClientAuthenticationMethod> metadataAuthMethods = metadata.getTokenEndpointAuthMethods();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another validation check can be applied, see 4.3. OpenID Provider Configuration Validation

The issuer value returned MUST be identical to the Issuer URL that was directly used to retrieve the configuration information. This MUST also be identical to the iss Claim value in ID Tokens issued from this Issuer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we don't use the Issuer provided by the Configuration, what is the benefit of validating this? It is something that is up to the Provider (we are a client) to ensure that they are the same unless we (the Client) end up using this, I don't think we should validate it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if there was a MITM scenario and the metadata response returned was from evil.com instead. The client would than use evil.com authorization and token endpoints

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jgrandja I believe you are saying that if the returned payload has an issuer that is different from the one used to make the request, then the entire response is suspect, even if we aren't using the issuer value in the response. I agree with this assessment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you aren't using HTTPS you are in trouble no matter what. A malicious user will respond with a valid issuer but an invalid endpoint of sorts. I went ahead and add the validation, but since we don't use the returned issuer, we are really validating the OpenID Configuration of the OP for the sake of validating vs any real gain.

@jgrandja
Copy link
Contributor

I'd like to make a proposal for enhancing this feature.

I see the need for using the OpenID Provider Metadata in other features as well.

For example, I have an outstanding task for ID Token validation, see this comment

Also, we may leverage some of the signature/encryption related meatadata in one or more JwtDecoder's on the Resource Server side or possible on Client side for ID Token verification.

What if we extracted some of the code in OidcConfigurationProvider into:

package org.springframework.security.oauth2.client.oidc.discovery;


public final class OidcProviderDiscovery {

	public static OidcProviderMetadata discover(String issuer) {
		OidcProviderMetadata providerMetadata = new OidcProviderMetadata();

		// TODO Obtain OpenID Provider Configuration Information



		return providerMetadata;
	}

	public static class OidcProviderMetadata {

	}
}

This way OidcConfigurationProvider can leverage OidcProviderDiscovery and it can be further leveraged in features implemented at a later point.

What do you think?

@rwinch
Copy link
Member Author

rwinch commented May 18, 2018

@jgrandja I am not sure if this makes sense. I was thinking that we will eventually add additional information to the ClientRegistration to account for things ike signature/encryption related metadata. From my perspective, all the information obtained from the endpoint is registration information. In any event, I'd suggest we hold off until we get that far.

@jgrandja
Copy link
Contributor

I was thinking that we will eventually add additional information to the ClientRegistration to account for things like signature/encryption related metadata.

I'm not seeing that we would add this to ClientRegistration. A lot of that information in the Provider Metadata is not necessarily ClientRegistration information. Some of it is provider specific info and we would use OidcProviderMetadata in other features that are not contained in the ClientRegistration. I want to be careful that we don't bloat ClientRegistration too much.

Either way, we can hold off on this. I just figure it would be good to get this done now since you were in this already.

rwinch added a commit that referenced this pull request May 18, 2018
rwinch added a commit that referenced this pull request May 18, 2018
rwinch added a commit that referenced this pull request May 18, 2018
rwinch added a commit that referenced this pull request May 18, 2018
rwinch added a commit that referenced this pull request May 18, 2018
Validate the issuer that was returned matches the issuer that was
was requested.

Issue: gh-5355
@rwinch
Copy link
Member Author

rwinch commented May 18, 2018

@sparty02 Now that this is merged into master, I created a Boot ticket for the Auto Configuration Support spring-projects/spring-boot#13210

kazuki43zoo added a commit to kazuki43zoo/spring-security that referenced this pull request May 19, 2018
rwinch pushed a commit that referenced this pull request May 21, 2018
@rwinch rwinch added the type: enhancement A general enhancement label May 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: config An issue in spring-security-config in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Create ClientRegistration from OIDC Discovery
5 participants