Expected Behavior
For a JWT based resource server it is possible to configure a custom jwt authentication converter like this:
@Override
protected void configure(HttpSecurity http) throws Exception {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and().authenticationProvider()
.csrf().disable()
.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(new MyAuthenticationConverter());
}
MyAuthenticationConverter converts a JWT object into a instance of AbstractAuthenticationToken.
The converter is able to set the details for the Authentication to allow for custom information. The JwtAuthenticationProvider should only set the details with the token with BearerTokenAuthenticationToken.getDetails() if there is no custom object defined.
Current Behavior
The details set by the JWT converter is always overwritten by the JwtAuthenticationProvider.
Context
I like to define a custom type that carries application specific authentication details that can then be used in the actual code (like a controller).
var details = (MyDetails) authentication.getDetails()
Comment From: jzheaux
Hi, @achimgrimm, can you explain more of what you are trying to do? The reason I ask is that the details object is intended for authentication request details:
/**
* Stores additional details about the authentication request. These might be an IP
* address, certificate serial number etc.
* @return additional details about the authentication request, or <code>null</code>
* if not used
*/
It seems odd that an authentication provider would specify details about the authentication request.
Generally speaking, if you have custom information to add, you create a custom authentication implementation.
Comment From: achimgrimm
Hi @jzheaux,
I like to use the authentication converter to extract information from the JWT and provide it to my application.
For the authorities it works fine. I check the roles claim and transform it into the authorities.
For the principal it works firn. I check the uid claim and use it as a principal.
But if I set additional information (about the authentication request) it gets overwritten by the JwtAuthenticationProvider. As the converter returns a JwtAuthenticationToken that carries information about the principal, authorities and some details, it should be possible for the converter to actually define those details.
While checking the API another time, I found the documentation for the getPrincipal.
java.lang.Object getPrincipal()
The identity of the principal being authenticated. In the case of an authentication request with username and password, this would be the username. Callers are expected to populate the principal for an authentication request.
The AuthenticationManager implementation will often return an Authentication containing richer information as the principal for use by the application. Many of the authentication providers will create a UserDetails object as the principal.
Returns:
the Principal being authenticated or the authenticated principal after authentication.
Maybe this would be a better fit for my application to carry the information about the user. But, as right now, that principal is also always the JWT token. As the JwtAuthenticationToken offers no constructor to set the principal as the the AbstractOAuth2TokenAuthenticationToken does.
The actual change would be an additional constructor in the JwtAuthenticationToken to allow the converter to pass a principal object.
Comment From: jzheaux
Maybe this would be a better fit for my application to carry the information about the user.
Agreed that this is usually where details about the user are stored.
But, as right now, that principal is also always the JWT token
I wonder if BearerTokenAuthentication would be a better fit for you since it takes an OAuth2AuthenticatedPrincipal as a principal. Being an interface, it gives a lot of flexibility for mapping to your underlying domain.
You can map a JwtAuthenticationToken like so:
class MyConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private final JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
AbstractAuthenticationToken token = this.authenticationConverter.convert(jwt);
OAuth2AuthenticatedPrincipal principal = myCustomUserLookup(jwt);
OAuth2AccessToken accessToken = new OAuth2AccessToken(
OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt());
return new BearerTokenAuthentication(principal, accessToken, token.getAuthorities());
}
}
Or, you can simply create your own Authentication implementation for additional flexibility and return that instead.
Comment From: achimgrimm
I changed my code with your suggestion. It works perfectly. Thank you very much for the support.