Skip to content
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

Expose ID Token JwtDecoderFactory #6379

Closed
jgrandja opened this issue Jan 9, 2019 · 13 comments
Closed

Expose ID Token JwtDecoderFactory #6379

jgrandja opened this issue Jan 9, 2019 · 13 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) type: enhancement A general enhancement
Milestone

Comments

@jgrandja
Copy link
Contributor

jgrandja commented Jan 9, 2019

DefaultJwtDecoderFactory in OidcAuthorizationCodeAuthenticationProvider and OidcAuthorizationCodeReactiveAuthenticationManager is responsible for providing the JwtDecoder used for ID Token verification. Both are declared as private static.

The user may need to customize the JwtDecoder in certain scenarios, for example, configuring a clock skew (#5839). Given this, we should extract both DefaultJwtDecoderFactory to allow for reuse and customization/configuration.

@jgrandja jgrandja self-assigned this Jan 9, 2019
@jgrandja jgrandja added New Feature in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) labels Jan 9, 2019
@jgrandja jgrandja added this to the 5.2.0.M1 milestone Jan 9, 2019
@raphaelDL
Copy link
Contributor

Hi Joe, is this already taken?

@jgrandja
Copy link
Contributor Author

jgrandja commented Jan 9, 2019

I was planning on it but would be happy to hand it over to you :)

@jgrandja
Copy link
Contributor Author

jgrandja commented Jan 9, 2019

The 2 things to keep in mind for this task:

  • The name DefaultJwtDecoderFactory will need to be renamed to be more specific. Maybe OidcIdTokenDecoderFactory?
  • The user should be able to configure OidcIdTokenValidator.setClockSkew() per provider if required. This means that each OidcIdTokenValidator may have a different clock skew setting. When you think about it, if the application has Google and Okta configured for OIDC than they may need to configure different clock skew setting. This will be the tricky part.

@jgrandja
Copy link
Contributor Author

@raphaelDL Just a heads up that I'm hoping to get this merged before end of day Monday as 5.2.0.M1 is being released on Tue. Do you think you'll be able to have something for review by end of day today or tomorrow morning? No pressure at all if you don't have the cycles. Either way let me know and if you don't have the time than I can wrap this up fairly quickly.

@raphaelDL
Copy link
Contributor

Sure, I'm working on it, can you give me ideas on the multiple clock skew settings in OidcIdTokenValidator ? I can do it if you outline that for me... or I can leave it to you, whatever you feel like. I completely understand the schedule.

@jgrandja
Copy link
Contributor Author

As far as being able to support a different clock skew setting per provider, I think this will do the trick.

public final class OidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
   // This provides the default
   private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = OidcIdTokenValidator::new;

   // The user can supply their own factory that will allow them to adjust the clock skew or whatever else they choose to customize
   public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory) {
      ...
   }
}

Let's go with this for now and we can refine from here.
Thanks!

@jgrandja
Copy link
Contributor Author

Btw, the current implementations compose 2 OAuth2TokenValidator as follows:

OAuth2TokenValidator<Jwt> jwtValidator = new DelegatingOAuth2TokenValidator<>(new JwtTimestampValidator(), new OidcIdTokenValidator(clientRegistration));

JwtTimestampValidator is redundant so we don't need it. The new implementations should only contain OidcIdTokenValidator as the default, as per example above.

@raphaelDL
Copy link
Contributor

Ok Joe, thank you for your help.. I committed additional changes. I'm working on a test for the customization

@raphaelDL
Copy link
Contributor

raphaelDL commented Jan 10, 2019

hey Joe, I was looking at the code of setJwtValidatorFactory I wrote it as a pure setter, but thinking on the scenario when the user wants to customize the validator when the decoder is already built, shouldn't it be something like this???

public final void setJwtValidatorFactory(Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtCustomValidatorFactory, ClientRegistration clientRegistration) {
		jwtDecoders.computeIfPresent(clientRegistration.getRegistrationId(), ( key , decoder ) ->
		{
			OAuth2TokenValidator<Jwt> jwtCustomValidator = jwtCustomValidatorFactory.apply(clientRegistration);
			NimbusReactiveJwtDecoder jwtDecoder = new NimbusReactiveJwtDecoder(
					clientRegistration.getProviderDetails().getJwkSetUri());
			jwtDecoder.setJwtValidator(jwtCustomValidator);
		return jwtDecoder;
		});
	}

maybe I am missing something? what do you think?

@jgrandja
Copy link
Contributor Author

the scenario when the user wants to customize the validator when the decoder is already built

I'm not sure I follow? When the JwtDecoder is built for a specific ClientRegistration than the jwtValidatorFactory will get called to associate the validator. This only happens once. If the user called setJwtValidatorFactory() with a different validator factory than it will never get called again for the previously built ClientRegistration because when createDecoder is called the second time than it's found in the Map and returned. Makes sense?

@raphaelDL
Copy link
Contributor

Yes I understand, I was thinking what happens when yo do this:

you call setJwtValidatorFactory(custom), The custom validator is no longer the default
then call createDecoder(client_reg_1). created with a custom validator
then call createDecoder(client_reg_2). It uses the previous validator meant for the client_reg_1 ? shouldn't it be the default?
am I lost? :s

@jgrandja
Copy link
Contributor Author

This is what I'm envisioning the user would provide:

public Function<ClientRegistration, OAuth2TokenValidator<Jwt>> customJwtValidatorFactory() {
	return clientRegistration -> {
		OidcIdTokenValidator idTokenValidator = new OidcIdTokenValidator(clientRegistration);
		if (clientRegistration.getRegistrationId().equals("google")) {
			idTokenValidator.setClockSkew(Duration.ofSeconds(30));
		} else if (clientRegistration.getRegistrationId().equals("okta")) {
			idTokenValidator.setClockSkew(Duration.ofSeconds(60));
		} else {
			// Default clock skew
		}
		return idTokenValidator;
	};
}

With this custom jwtValidatorFactory, the user has set a different clock skew for google and okta and all other created validators keep the default.

Makes sense?

@raphaelDL
Copy link
Contributor

it totally makes sense, thanks.... then I should add the tests and open the PR, thanks

raphaelDL added a commit to raphaelDL/spring-security that referenced this issue Jan 14, 2019
This commit ensures that the JwtDecoder is not a private field inside
the Oidc authentication provider by extracting this class and giving the
possibility to customize the way different providers are validated.

Fixes: spring-projectsgh-6379
@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: 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 a pull request may close this issue.

3 participants