-
Notifications
You must be signed in to change notification settings - Fork 6k
No easy way to set SecurityContextRepository on OAuth2ResourceServerConfigurer #11919
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
Ok, I've dug further into this and for my particular case I think I'm going to have to re-engineer more as |
I believe your analysis is correct @buckett, thanks for providing an update. Note that you can use an ObjectPostProcessor<BearerTokenAuthenticationFilter> postProcessor = new ObjectPostProcessor<>() {
@Override
public <O extends BearerTokenAuthenticationFilter> O postProcess(O filter) {
filter.setSecurityContextRepository(...);
return filter;
}
};
http.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
.withObjectPostProcessor(postProcessor)
); I'm going to close this issue for now, based on your last comment. |
We had the very same problem and worked around it temporarily with an ugly hack.. @buckett - did you find a clean approach on how to achieve creating a session and saving an authentication to it after ResourceServer with BearerTokenAuth succeeds? Our Workaround: ..
.and().oauth2ResourceServer(oauth2 -> oauth2.withObjectPostProcessor(bearerTokenAuthPostProcessor).opaqueToken()); /**
* We first configure the BearerTokenAuthenticationFilter,
* so that it uses a HttpSessionSecurityContextRepository (instead of NullSecurityContextRepository).
* By default the BearerTokenAuthenticationFilter would not write anything to the session.
* <br/>
* Also we need to extend the default HttpSessionSecurityContextRepository,
* to swap the BearerTokenAuthentication before calling saveContext, so that it actually gets saved later.
*/
ObjectPostProcessor<BearerTokenAuthenticationFilter> bearerTokenAuthPostProcessor = new ObjectPostProcessor<>() {
@Override
public <O extends BearerTokenAuthenticationFilter> O postProcess(O filter) {
filter.setSecurityContextRepository(new HttpSessionSecurityContextRepository() {
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
Authentication authentication = context.getAuthentication();
NonTransientBearerTokenAuthentication nonTransientBearerTokenAuthentication = new NonTransientBearerTokenAuthentication(
(OAuth2AuthenticatedPrincipal) authentication.getPrincipal(),
(OAuth2AccessToken) authentication.getCredentials(),
authentication.getAuthorities()
);
context.setAuthentication(nonTransientBearerTokenAuthentication);
super.saveContext(context, request, response);
}
});
return filter;
}
};
/**
* Copy of {@link org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication} without @Transient,
* so that @{@link HttpSessionSecurityContextRepository#saveContext(SecurityContext, HttpServletRequest, HttpServletResponse)}
* and later SaveToSessionResponseWrapper#saveContext
* do continue successfully, when HttpSessionSecurityContextRepository#isTransient is checked there.
*/
private static class NonTransientBearerTokenAuthentication extends AbstractOAuth2TokenAuthenticationToken<OAuth2AccessToken> {
private final Map<String, Object> attributes;
public NonTransientBearerTokenAuthentication(OAuth2AuthenticatedPrincipal principal, OAuth2AccessToken credentials,
Collection<? extends GrantedAuthority> authorities) {
super(credentials, principal, credentials, authorities);
Assert.isTrue(credentials.getTokenType() == OAuth2AccessToken.TokenType.BEARER, "credentials must be a bearer token");
this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(principal.getAttributes()));
setAuthenticated(true);
}
@Override
public Map<String, Object> getTokenAttributes() {
return this.attributes;
}
} |
@icyerasor I've possibly forgotten something but we did something very similar, created a persistable JWT token (like your /**
*
* This was copied from Spring, but changed so that it returned a persistable JWT. This allows the authentication
* to be stored in the session.
*
* @author Rob Winch
* @author Josh Cummings
* @author Evgeniy Cheban
* @author Olivier Antoine
* @author Matthew Buckett
* @since 5.1
*/
public class PersistableJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
private Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
private String principalClaimName = JwtClaimNames.SUB;
@Override
public final AbstractAuthenticationToken convert(Jwt jwt) {
Collection<GrantedAuthority> authorities = extractAuthorities(jwt);
String principalClaimValue = jwt.getClaimAsString(this.principalClaimName);
return new PersistableJwtAuthenticationToken(jwt, authorities, principalClaimValue);
}
/**
* Extracts the {@link GrantedAuthority}s from scope attributes typically found in a
* {@link Jwt}
* @param jwt The token
* @return The collection of {@link GrantedAuthority}s found on the token
* @deprecated Since 5.2. Use your own custom converter instead
* @see JwtGrantedAuthoritiesConverter
* @see #setJwtGrantedAuthoritiesConverter(Converter)
*/
@Deprecated
protected Collection<GrantedAuthority> extractAuthorities(Jwt jwt) {
return this.jwtGrantedAuthoritiesConverter.convert(jwt);
}
/**
* Sets the {@link Converter Converter<Jwt, Collection<GrantedAuthority>>}
* to use. Defaults to {@link JwtGrantedAuthoritiesConverter}.
* @param jwtGrantedAuthoritiesConverter The converter
* @since 5.2
* @see JwtGrantedAuthoritiesConverter
*/
public void setJwtGrantedAuthoritiesConverter(
Converter<Jwt, Collection<GrantedAuthority>> jwtGrantedAuthoritiesConverter) {
Assert.notNull(jwtGrantedAuthoritiesConverter, "jwtGrantedAuthoritiesConverter cannot be null");
this.jwtGrantedAuthoritiesConverter = jwtGrantedAuthoritiesConverter;
}
/**
* Sets the principal claim name. Defaults to {@link JwtClaimNames#SUB}.
* @param principalClaimName The principal claim name
* @since 5.4
*/
public void setPrincipalClaimName(String principalClaimName) {
Assert.hasText(principalClaimName, "principalClaimName cannot be empty");
this.principalClaimName = principalClaimName;
}
} and then use this when configuring the security: .oauth2ResourceServer().jwt().jwtAuthenticationConverter(new PersistableJwtAuthenticationConverter()).and() I can't see anything else much that got changed in the commits. |
Okay, nice. |
Expected Behavior
Easy way to set the
SecurityContextRepository
on theBearerTokenAuthenticationFilter
when usingOAuth2ResourceServerConfigurer
Current Behavior
You can't use
OAuth2ResourceServerConfigurer
and so have to manually set things up.Context
With #10949 the
BearerTokenAuthenticationFilter
class can have aSecurityContextRepository
set, however when theBearerTokenAuthenticationFilter
is created byOAuth2ResourceServerConfigurer
there's no easy way to set theSecurityContextRepository
It used to be the case before #10949 that the request authentication would get stored in the session, however with newer versions this isn't happening and it appears that to re-enable this we have to set the
SecurityContextRepository
.The text was updated successfully, but these errors were encountered: