'Spring Security: How to use a UserDetailsService with JwtAuthenticationProvider?
I have a REST service, written using Spring MVC. The server is an OAuth2 resource server and I am using the JwtAuthenticationProvider to have the JWT parsed and turned into the Principal. This all works fine.
However, what I really want to do is to load user details from a database, using the username provided from a Claim in the JWT. Then that new Principal should replace or (ideally) wrap the Jwt so that it is available directly from the SecurityContext.
I am really struggling to see how to do this. The JwtAuthenticationProvider does not seem to work with a UserDetailsService. I also looked at doing this with a Converter - but it is not easy to extend JwtAuthenticationConverter because the convert method is final (why?).
So to be very clear, here is what I ideally want to happen:
- Bearer token is presented to service.
- Parse Jwt and extract claims
- Use one of these claims as a key to my user database, where I can look up attributes, entitlements etc
- Turn these into a new Principal object which is available in the SecurityContext's Authentication object.
The configure method in my WebSecurityConfigurerAdapter has this:
http.authorizeRequests().antMatchers("/api/*").authenticated().and().oauth2ResourceServer().jwt();
I cannot be the only person who wants to use a user database along with OAuth2, so I must be missing something fundamental? I am using Spring Security 5.2.0.
Solution 1:[1]
The JwtAuthenticationProvider
does not support an UserDetailsService
because in theory you are not supposed to have UserDetails
in an application that does not manage credentials. I'm not saying that you cannot have any type of user, but the UserDetailsService
will not be used or autowired by Spring Security.
You could register a bean of type JwtAuthenticationConverter
, do whatever you need in the convert
method and return your custom authentication token, like so:
@Component
public class JwtAuthenticationConverterAdapter implements Converter<Jwt, AbstractAuthenticationToken> {
private JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
@Override
public AbstractAuthenticationToken convert(Jwt jwt) {
var token = this.jwtAuthenticationConverter.convert(jwt);
// build your custom token with the properties from the token above
return customToken;
}
}
Solution 2:[2]
I agree with your concerns - and I have found it useful to override Spring's default processing. There is a claims extensibility pattern I use with some providers, where JWT handling is only one part.
I have a Spring Boot code sample that you can run - it uses a custom filter and Connect2Id classes - OAuth integration is described here. Happy to answer any follow up questions if it helps
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Marcus Hert da Coregio |
Solution 2 |