Skip to content

Improve handling of non-String principal claim values #9212

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
omlip opened this issue Nov 21, 2020 · 8 comments · Fixed by #9215
Closed

Improve handling of non-String principal claim values #9212

omlip opened this issue Nov 21, 2020 · 8 comments · Fixed by #9215
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

@omlip
Copy link

omlip commented Nov 21, 2020

Context

When configuring an Spring application as an OAuth Resource Server and we use Jwt, we can set manually the principalClaimName on JwtAuthenticationConverter object.

Example like this

fun getCustomAuthenticationConverter(authoritiesClaimName : String): Converter<Jwt, Mono<AbstractAuthenticationToken>> {
    return ReactiveJwtAuthenticationConverterAdapter(
            JwtAuthenticationConverter().apply {
                setPrincipalClaimName("anInt")
                setJwtGrantedAuthoritiesConverter(GrantedAuthoritiesExtractor(authoritiesClaimName))
            }
    )
}

Current Behavior

If we use a specific principalClaimName on a JwtAuthenticationConverter and if the value behind the claim name is not a String, a ClassCastException is thrown in the convert method.

	@Override
	public final AbstractAuthenticationToken convert(Jwt jwt) {
		Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
		if (this.principalClaimName == null) {
			return new JwtAuthenticationToken(jwt, authorities);
		}
		String name = jwt.getClaim(this.principalClaimName); // this line throw a ClassCastException
		return new JwtAuthenticationToken(jwt, authorities, name);
	}

We can easily reproduce the situation by adding a test in class JwtAuthenticationConverterTests.

	@Test
	public void convertWhenPrincipalClaimNameSetWithOtherValueThanString() {
		this.jwtAuthenticationConverter.setPrincipalClaimName("user_id");
		Jwt jwt = TestJwts.jwt().claim("user_id", 100).build();
                //TODO: complete with assertions
	}

Expected Behavior

I think we should adapt the code to not lead to a ClassCastException but maybe throw a proper exception which "explain" what the problem is.

Maybe by adapting like this ?

Object claimValue = jwt.getClaim(this.principalClaimName);
Assert.isInstanceOf(String.class, claimValue, "The principal value is not a String");

String name = (String)claimValue;

or maybe with the usage of Jwt.claimAsString(...) method ?

What do you think ?
Let me know if I can help.

PS : by the way, regarding this RFC https://tools.ietf.org/html/rfc7519#section-4.1.2 about JWT token, nothing say that a claim value must be a String as it is implemented in JwtAuthenticationToken. Maybe this discussion is out of scope but indeed

Best regards,
Olivier

@omlip omlip added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Nov 21, 2020
@jgrandja
Copy link
Contributor

@omlip I agree that we should avoid a ClassCastException from being thrown.

I think adding Assert.isInstanceOf() (as suggested) is a reasonable improvement. Would you be interested in submitting a PR for this?

@jgrandja jgrandja added in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 23, 2020
@omlip
Copy link
Author

omlip commented Nov 23, 2020

@jgrandja Sure I will, you can assign it to me

@jgrandja
Copy link
Contributor

Thanks @omlip !

@omlip
Copy link
Author

omlip commented Nov 23, 2020

@jgrandja PR submitted

Sould not we, by the way, adjust the documentation to mention that it is possible to set manually a principalClaimName and that must be a String ?

@jgrandja
Copy link
Contributor

Thanks @omlip. Agreed, please update the javadoc for setPrincipalClaimName().

@omlip
Copy link
Author

omlip commented Nov 24, 2020

Ok will update the PR accordingly.

@omlip
Copy link
Author

omlip commented Nov 24, 2020

Hi @jgrandja I was talking about Spring Security Reference documentation.
In particular, the following sentence:

The resulting Authentication#getPrincipal, by default, is a Spring Security Jwt object, and Authentication#getName maps to the JWT’s sub property, if one is present.

to be transformed in something like this :

The resulting Authentication#getPrincipal, by default, is a Spring Security Jwt object, and Authentication#getName maps to the JWT’s sub property, if one is present. If none is present, you can still set the principal claim name to any JWT's property by using setPrincipalClaimName method. The value behind the claim must be a String

but also I can update javadoc of course :-)

What do you think ?

br
Olivier

@jgrandja
Copy link
Contributor

Sounds good @omlip

@jgrandja jgrandja added this to the 5.5.0-M2 milestone Nov 26, 2020
omlip pushed a commit to omlip/spring-security that referenced this issue Dec 1, 2020
@jzheaux jzheaux changed the title Better meaning error when principalClaimName is set and claim value is not a String Improve handling of non-String principal claim values Dec 2, 2020
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.

2 participants