In microservice architecture, we usually use OAuth 2.0 to generate access and refresh tokens for users. For trusted systems, where we have server-to-server communication, we use system tokens(Client Credentails). However, OAuth 2.0 doesn’t return a refresh token in these cases, so we can’t use a refresh token to get a new access token. Since there is no direct way to do this, we are customizing the OAuth 2.0 implementation.
Access Token
An access token is a credential that is used to access protected resources on behalf of the resource owner. It is issued by the authorization server and is typically a string that represents the authorization granted to the client. The access token has a limited lifetime and scope, meaning it can only be used for specific resources and actions, and it expires after a certain period.
Refresh Token
A refresh token is a credential used to obtain a new access token without requiring the resource owner to re-authenticate. It is also issued by the authorization server and is typically a long-lived token that can be exchanged for a new access token when the current access token expires or becomes invalid. Refresh tokens help improve the user experience by allowing seamless re-authorization without repeatedly prompting the user for credentials.
Implementation
Implement custom ClientCredentialsTokenGranter to generate access token and refresh token with grant validation.
public class CustomClientCredentialsTokenGranter extends ClientCredentialsTokenGranter {
private ClientDetailsService clientDetailsService;
private String grantType = "client_credentials";
public CustomClientCredentialsTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
super(tokenServices, clientDetailsService, requestFactory);
this.clientDetailsService=clientDetailsService;
}
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (!this.grantType.equals(grantType)) {
return null;
} else {
String clientId = tokenRequest.getClientId();
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
this.validateGrantType(grantType, client);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Getting access token for: " + clientId);
}
return this.getAccessToken(client, tokenRequest);
}
}
}
This method, getCustomTokenGranters
, configures and returns a list of custom token granters for an OAuth2 authorization server. It sets up various types of token granters to handle different OAuth2 grant types:
- Authorization Code Token Granter: Handles the authorization code grant type.
- Refresh Token Granter: Handles the refresh token grant type.
- Implicit Token Granter: Handles the implicit grant type.
- Custom Client Credentials Token Granter: A custom implementation for the client credentials grant type.
- Resource Owner Password Token Granter: Handles the resource owner password credentials grant type, if an authentication manager is available.
These granters are added to a list and returned to configure the OAuth2 authorization server
private List<TokenGranter> getCustomTokenGranters(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
List<TokenGranter> tokenGranters = new ArrayList();
tokenGranters.add(new AuthorizationCodeTokenGranter(endpoints.getTokenServices(), endpoints.getAuthorizationCodeServices(), endpoints.getClientDetailsService(), requestFactory));
tokenGranters.add(new RefreshTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), requestFactory));
ImplicitTokenGranter implicit = new ImplicitTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), requestFactory);
tokenGranters.add(implicit);
CustomClientCredentialsTokenGranter customClientCredentialsTokenGranter= new CustomClientCredentialsTokenGranter(tokenServices(tokenStore, clientDetailsService), clientDetailsService,requestFactory);
tokenGranters.add(customClientCredentialsTokenGranter);
if (this.authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(this.authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), requestFactory));
}
return tokenGranters;
}
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
return new CompositeTokenGranter(getCustomTokenGranters(endpoints));
}
Add All Token granters with AuthorizationServerEndpointsConfigurer
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService)
.accessTokenConverter(jwtAccessTokenConverter())
.tokenEnhancer(tokenEnhancerChain)
.tokenStore(tokenStore())
.reuseRefreshTokens(false)
.tokenGranter(tokenGranter(endpoints));
}
Test Result
Generate System token (Client Credential) and now it will return both access token and refresh token so client can regenerate access token using refresh token using refresh api.
Generate access token using refresh token