-
Notifications
You must be signed in to change notification settings - Fork 6k
Add constructor to JwtAuthenticationToken that takes principal #7834
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
Comments
5.2 also introduced Given that, you might consider doing: Converter<Jwt, BearerTokenAuthentication> converter = jwt -> {
Collection<GrantedAuthority> authorities = // ...
OAuth2AccessToken token = new OAuth2AccessToken(BEARER,
jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt());
OAuth2AuthenticatedPrincipal principal = // ... custom principal
return new BearerTokenAuthentication(principal, token, authorities);
}; And then calling the Of course, if you need a completely custom class, the contract allows you to do that too: Converter<Jwt, FooAuthentication> converter = jwt -> {
FooUser user = // ...
return new FooAuthentication(jwt, user);
} |
I'm going to close this issue at this point, but feel free to still post here or reopen if you feel there's more to discuss. |
I understand that I can use But
Actually, I already done this. :-) I just copy-pasted public JwtAuthenticationToken(Jwt jwt, Object principal, Collection<? extends GrantedAuthority> authorities) {
super(jwt, principal, jwt, authorities);
this.setAuthenticated(true);
this.name = jwt.getSubject();
} I also copy-pasted Actually, in my custom It looks like this: public class JwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private final Converter<Jwt, AuthenticatedUser> jwtToPrincipalConverter;
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter
= new JwtGrantedAuthoritiesConverter();
public JwtAuthenticationConverter(Converter<Jwt, AuthenticatedUser> jwtToPrincipalConverter) {
Assert.notNull(jwtToPrincipalConverter, "jwtToPrincipalConverter cannot be null");
this.jwtToPrincipalConverter = jwtToPrincipalConverter;
}
public JwtAuthenticationConverter(
Converter<Jwt, AuthenticatedUser> jwtToPrincipalConverter,
Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
this(jwtToPrincipalConverter);
setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
}
@Override
public final AbstractAuthenticationToken convert(Jwt jwt) {
AuthenticatedUser principal = jwtToPrincipalConverter.convert(jwt);
Collection<GrantedAuthority> jwtAuthorities = jwtGrantedAuthoritiesConverter.convert(jwt);
Collection<GrantedAuthority> authorities = Stream.of(jwtAuthorities, principal.getAuthorities())
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.distinct()
.collect(ImmutableList.toImmutableList());
return new JwtAuthenticationToken(jwt, principal, authorities);
}
public void setJwtGrantedAuthoritiesConverter(Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
Assert.notNull(jwtGrantedAuthoritiesConverter, "jwtGrantedAuthoritiesConverter cannot be null");
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
}
} This fact means I need a custom Also you can see I made So, to be honest, I don't see how we can improve current implementation of I'm good with it. But |
@xak2000 Good questions, and thank you for the detailed explanation. You bring up a good point that
No, not terribly. I think in that case I'd want a constructor like: public JwtAuthenticationToken(Jwt jwt, Object principal, Object credentials, Collection<...> authorities) { So as to not push the semantics of the If I may turn around the question to you, does it seem unnatural to include a |
@jzheaux Thanks for the response!
Maybe you are right. It would be more general solution constructor than my version (that implies that The JWT can participate in authorization too (by using But if JWT is used only for 1st party clients, even this is not necessary. The only required claim in this case is
Honestly, I don't remember full context of the problem now, sorry. It was 2 months ago. At first glance it looks good. If If But, even then, |
Workaround to have a user / other object as principal: class MyUserJwtAuthenticationToken extends JwtAuthenticationToken {
private final MyUserType user;
public MyUserJwtAuthenticationToken(Jwt jwt, MyUserType user) {
super(jwt);
this.user = Objects.requireNonNull(user, "user");
}
@Override
public MyUserType getPrincipal() {
return user;
}
} You need to tell spring that this is a valid authentication: @Component
public class GlobalAuthenticationConfigurer extends GlobalAuthenticationConfigurerAdapter {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AuthenticationProvider() {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return authentication;
}
@Override
public boolean supports(Class<?> authentication) {
return MyUserJwtAuthenticationToken.class.isAssignableFrom(authentication);
}
});
}
} And you need to add the converter to your securtiy config, depending on the configure method you use: authenticationTokenConverter = jwt -> new MyUserJwtAuthenticationToken(jwt, /* code to find user using jwt.getSubject() */);
// ...
authenticationProvider.setJwtAuthenticationConverter(authenticationTokenConverter);
// ...
.jwt(jwtConfig -> jwtConfig.jwtAuthenticationConverter(
authenticationTokenConverter)); |
@michaelzangl Thanks for posting your solution. Can you elaborate on why registering an authentication provider is necessary? It seems like this should be sufficient: private Converter<Jwt, MyUserJwtAuthenticationToken> authenticationConverter() {
return jwt -> {
MyUser user = // .. get user
return new MyUserJwtAuthenticationToken(jwt, user);
};
}
// ...
http
// ...
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(authenticationConverter())
)
); |
@jzheaux Have you tested it? I don't know why, but when I added it to the application, the At some point I just stopped debugging and hacked this fix. |
@jzheaux Hi Josh, can you consider adding the constructor which takes the principal(custom object). |
Any updates on this issues in the meantime? It really surprises me that this has not moved forward. Isn't the principle intended to be the user domain object? There should be an easy way to set it. @michaelzangl You forgot to call However this is still not an acceptable workaround because |
There is a code in https://github.com/spring-projects/spring-security/blob/5.7.3/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurer.java#L405 OAuth2ResourceServerConfigurer ``` Converter<Jwt, ? extends AbstractAuthenticationToken> getJwtAuthenticationConverter() { if (this.jwtAuthenticationConverter == null) { if (this.context.getBeanNamesForType(JwtAuthenticationConverter.class).length > 0) { this.jwtAuthenticationConverter = this.context.getBean(JwtAuthenticationConverter.class); } else { this.jwtAuthenticationConverter = new JwtAuthenticationConverter(); } } return this.jwtAuthenticationConverter; } ``` What is benefit of **getBean(JwtAuthenticationConverter)** when JwtAuthenticationConverter has final key method? My business case is similar to: spring-projects#7834 I am quite satisfied how JWT is converted in JWTAuthenticationToken, but rest of my application needs custom Authentication or at least custom Principal. I'm going to implement own JwtAuthenticationConverter which takes JWTAuthenticationToken and returns something customized Current JwtAuthenticationConverter allows me only customize Authentication *name* and *authorities*. I want customize at least Principal at most whole Authentication
Currently,
JwtAuthenticationToken
always pass oneJwt
instance to super constructor (AbstractOAuth2TokenAuthenticationToken
) astoken
,principal
andcredentials
. So, that implies that all three values must always be a jwt token itself.I don't see any reason why
principal
in JWT token must always be the token itself. It already represents bothtoken
andcredentials
. But forprincipal
I want more flexibility: an ability to also load user (represented by JWT "sub" claim) from DB or external service.In my application I want to use a JWT token, but I also want to create a custom class that will represent a principal, like
UserDetials
implementation or just a new custom class.AbstractOAuth2TokenAuthenticationToken
has a constructor, that takestoken
,principal
andcredentials
separately.It would be good, if
JwtAuthenticationToken
also have a constructor, that at least takesprincipal
separately.If this will be added, then it will also be good if
JwtAuthenticationConverter
will have an optional propertywhich, if set, will be used to convert
Jwt
toprincipal
(by loading it from DB, external service, or just by creating a more application-friendlyUser
object fromJwt
token) before calling newJwtAuthenticationToken
constructor.The text was updated successfully, but these errors were encountered: