Skip to content

Auto-configure JwtDecoder via OpenId Configuration #5628

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

Closed
wants to merge 1 commit into from

Conversation

jzheaux
Copy link
Contributor

@jzheaux jzheaux commented Aug 1, 2018

Adding OpenIdConfigurationJwtDecoderProvider which takes an issuer
and derives from it the jwk set uri via a call to
.well-known/openid-configuration

Fixes: gh-5523

@jzheaux jzheaux requested review from jgrandja and rwinch August 1, 2018 22:17
* @author Josh Cummings
* @since 5.1
*/
public final class OpenIdConfigurationJwtDecoderProvider {
Copy link
Contributor

Choose a reason for hiding this comment

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

This class needs to be in the config module just like OidcConfigurationProvider is.

Also, the name of this class restricts it to JwtDecoder configuration only. I think we need to rename it to be more generic so it makes sense when we need other provider configuration and associated static factory methods.

The common naming convention is to prefix with Oidc not OpenId

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense to be consistent. Can you clarify why the existing OidcConfigurationProvider is in the config module, so I better understand this for the future? My guess is so that it can have broader access to security components across multiple modules.

As for the name, the only generic name that makes sense to me is OidcConfigurationProvider, just in a different package (since the existing one is in a client package). This is because they have the same object in mind: Use OIDC to discover and configure some security component. What are your thoughts on two classes of the same name, in different packages? Or what rules do we have for moving the existing OidcConfigurationProvider into a more generic package like o.s.s.config.oauth2.oidc.OidcConfigurationProvider which could then house both methods?

I we can combine the two, then I might like something named OidcDiscoveryConfigurationProvider because it clarifies the strategy being employed. Thoughts?

I also have a concern about the fact that, should these be combined (or future methods be added to either), the current method naming convention won't suffice. Could something like the following work instead:

public final class OidcConfigurationProvider {
    
    private OidcConfigurationProvider(OIDCProviderMetadata metadata) { ... }

    public static OidcConfigurationProvider issuer(String issuer) {
        String openidConfiguration = getOpenidConfiguration(issuer);
        OIDCProviderMetadata metadata = parse(openidConfiguration);
        // ... verify metadata
        return new OidcConfigurationProvider(metadata);
    }

    public JwtDecoder getJwtDecoder() {
        // ... derive from metadata
    }

    public ClientRegistration.Builder getClientRegistrationBuilder() {
        // ... derive from metadata
    }
}

The benefit of this approach is that users could create many security components without invoking the endpoint and doing the subsequent parsing and verification work more than once:

OidcConfigurationProvider.issuer("https://issuer").getClientRegistrationBuilder()

// or

OidcConfigurationProvider provider = OidcConfigurationProvider.issuer("https://issuer");
ClientRegistration.Builder builder = provider.getClientRegistrationBuilder();
JwtDecoder decoder = provider.getJwtDecoder();

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you clarify why the existing OidcConfigurationProvider is in the config module

This is considered Java Config, just like OAuth2ResourceServerConfigurer and OAuth2ClientConfigurer, hence it belonging in config module

the only generic name that makes sense to me is OidcConfigurationProvider

Yeah same for me. Although we have this ticket to rename to OidcProviderConfiguration

What are your thoughts on two classes of the same name, in different packages

I was thinking the same and initially it makes sense but at the same time having users (or Boot) to fully qualify the class name when both are used (Client and Resource Server) would be a pain. I'm not sure what other name we can use at the moment.

Or what rules do we have for moving the existing OidcConfigurationProvider into a more generic package like o.s.s.config.oauth2.oidc.OidcConfigurationProvider which could then house both methods?

Housing both methods in OidcConfigurationProvider would require both dependencies - client and resource server - which is not good especially if the user is only using client for example.

However, I believe @rwinch did suggest this strategy (housing in one config class) when we spoke? Maybe he can clarify this further as I'm not sure if I understood him correctly.

I we can combine the two, then I might like something named OidcDiscoveryConfigurationProvider

I think we should use the name OidcProviderConfiguration to align with the spec. Or at least have that in the class name.

I do like your implementation approach on caching the OIDCProviderMetadata as per

public static OidcConfigurationProvider issuer(String issuer)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we should use the name OidcProviderConfiguration to align with the spec. Or at least have that in the class name.

My bad, I had misread the spec when I saw "Discovery" in the title [1], forgetting that we are just focused on the configuration section.

[1] - https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery

*/
public static JwtDecoder issuer(String issuer) {
String openidConfiguration = getOpenidConfiguration(issuer);
OIDCProviderMetadata metadata = parse(openidConfiguration);
Copy link
Contributor

Choose a reason for hiding this comment

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

The code in here is duplicated from OidcConfigurationProvider, the getOpenidConfiguration and parse methods. We should try to reuse.

@@ -6,6 +6,7 @@ dependencies {
compile springCoreDependency
compile 'com.nimbusds:nimbus-jose-jwt'

optional 'com.nimbusds:oauth2-oidc-sdk'
Copy link
Contributor

Choose a reason for hiding this comment

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

This dependency does not make sense. However, this will be removed when moving OpenIdConfigurationJwtDecoderProvider to the config module

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For the record, @jgrandja and I discussed, and this will stay here in oauth2-jose since JwtDecoder, the security component being instantiated by JwtDecoders, is in oauth2-jose.

@@ -111,6 +111,10 @@ public Jwt decode(String token) throws JwtException {
throw new JwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
}

public String getJwkSetUrl() {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not used anywhere. We should remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jgrandja please consult one of the unit tests, which confirms that the configuration took effect. The two alternatives are to use reflection to confirm this setting or to perform some heavier configuration to mock this url endpoint in addition to the openid-configuration endpoint already mocked.

I'm okay with the first option, though not precisely ideal, though the second option I believe increases the scope of the test too much.

What would you do in this situation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, I believe this situation would be simplified if we could expose RestOperations as configurable here as well as in NimbusJwtDecoderJwkSupport. Then, this test could provide a mock RestOperations and alleviate the need to mock an underlying server.

That being the case, I'm also okay with leaving the unit test in a suboptimal state (calling reflectively a private method) in anticipation of the class under test being able to more easily configure mock endpoints.

Copy link
Contributor

Choose a reason for hiding this comment

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

I must be missing something? I don't see any references to getJwkSetUrl() after searching for usages within IDE.

I believe this situation would be simplified if we could expose RestOperations as configurable here as well as in NimbusJwtDecoderJwkSupport

This is coming in #5603

@jgrandja
Copy link
Contributor

jgrandja commented Aug 2, 2018

@jzheaux Also, please consider this comment when re-working this PR

NOTE: We may have to re-factor the existing OidcConfigurationProvider to support both configurations of ClientRegistration and JwtDecoder. However, we're also debating on keeping these in separate classes - client and resource server specific configs. It's difficult to tell at this point until the work get's started so any suggestions you have let's start that dialogue in a PR.

@jzheaux jzheaux force-pushed the gh-5523 branch 8 times, most recently from f2224a8 to 5f52fe5 Compare August 9, 2018 15:34
@jgrandja
Copy link
Contributor

jgrandja commented Aug 9, 2018

@jzheaux Looks like you included the commit Jwt Claim Validation in this PR. Can you please remove that and re-submit.

@jzheaux
Copy link
Contributor Author

jzheaux commented Aug 9, 2018

Regarding including the Jwt Claim Validation commit, that is only temporary. It's a little messy, but since this is dependent on that, I rebased off of it so that the PR would compile. Once we merge the validation PR, I'll remove that commit here.

@jzheaux jzheaux force-pushed the gh-5523 branch 2 times, most recently from 86ab70e to 7e89f40 Compare August 16, 2018 19:04
Adding JwtDecoders#fromOidcIssuerLocation which takes an issuer
and derives from it the jwk set uri via a call to
.well-known/openid-configuration

Fixes: spring-projectsgh-5523
@jzheaux
Copy link
Contributor Author

jzheaux commented Aug 16, 2018

@rwinch This is ready for your review.

@rwinch rwinch self-assigned this Aug 20, 2018
@rwinch rwinch added this to the 5.1.0.RC1 milestone Aug 20, 2018
@rwinch
Copy link
Member

rwinch commented Aug 20, 2018

Thanks this is merged into master

@rwinch rwinch closed this Aug 20, 2018
@jzheaux jzheaux deleted the gh-5523 branch August 24, 2018 14:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support auto-configuration of JwkSet Uri
3 participants