I am working on implementing access and refresh tokens with OAuth 2.0. I have implemented a custom grant type, and I can renew the access token using a refresh token with already existing OAuth2RefreshTokenAuthenticationConverter
and OAuth2RefreshTokenAuthenticationProvider
. The problem is that, before granting a new access token, I need to call the database to verify that the user is still active and perform additional checks (e.g., to determine if they have lost their login eligibility in the meantime). Ideally, I need a call to authenticationManager.authenticate before granting a new access token.
I've tried to implement a OncePerRequest
filter for additional validation, but inside it I have no access to the user's username, only to the refresh token, grant type, and scope.
Is there a filter which accepts OAuth2AuthenticationToken and runs just before OAuth2RefreshTokenAuthenticationProvider
does?
I also considered copying the entire OAuth2RefreshTokenAuthenticationConverter
and adjusting it accordingly. However, I would like to avoid creating a copy of the converter due to maintainability concerns.
Thanks in advance!
Write a custom AuthenticationProvider to perform additional checks. It will run before OAuth2RefreshTokenAuthenticationProvider
runs. For example:
/**
* If the user account is locked, then throws an exception, which stops subsequent
* {@link AuthenticationProvider#authenticate AuthenticationProvider}s being tried.
*/
@RequiredArgsConstructor
public class UserDetailsRefreshTokenAuthenticationProvider implements AuthenticationProvider {
private final CustomUserDetailsService userDetailsService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
var authenticationToken = (OAuth2RefreshTokenAuthenticationToken) authentication;
String refreshToken = authenticationToken.getRefreshToken();
if (userDetailsService.isAccountLocked(refreshToken)) {
throw new LockedException("User account is locked");
}
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return OAuth2RefreshTokenAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Add this custom AuthenticationProvider into the token endpoint:
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(
HttpSecurity http,
UserDetailsRefreshTokenAuthenticationProvider userDetailsRefreshTokenAuthenticationProvider
) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.tokenEndpoint(endpoint -> endpoint
.authenticationProvider(userDetailsRefreshTokenAuthenticationProvider)
// ...