How OAuth2 Token Validation, Generation, and Refresh Really Work
This article explains the complete OAuth2 token lifecycle—including how resource servers validate incoming tokens, how the authorization server creates and reuses access tokens, and the mechanisms for passive and active token refresh—complete with Java code examples and practical client‑side strategies.
Token Validation Logic
// CheckTokenEndpoint.checkToken
@RequestMapping(value = "/oauth/check_token")
@ResponseBody
public Map<String, ?> checkToken(@RequestParam("token") String value) {
// Retrieve full token information from tokenStore
OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);
if (token == null) {
throw new InvalidTokenException("Token was not recognised");
}
if (token.isExpired()) {
throw new InvalidTokenException("Token has expired");
}
// Load authentication and authorities associated with the token
OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());
return accessTokenConverter.convertAccessToken(token, authentication);
}When a client sends a token in the request header, the resource server intercepts it, forwards the token to the authorization server’s check_token endpoint, and returns an error if the token is invalid or expired.
Token Generation Logic
// DefaultTokenServices.createAccessToken logic
public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
// Look for an existing token for the user
OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
OAuth2RefreshToken refreshToken = null;
if (existingAccessToken != null) {
// If the existing token is expired, remove it (and its refresh token if present)
if (existingAccessToken.isExpired()) {
if (existingAccessToken.getRefreshToken() != null) {
refreshToken = existingAccessToken.getRefreshToken();
tokenStore.removeRefreshToken(refreshToken);
}
tokenStore.removeAccessToken(existingAccessToken);
} else {
// Return the still‑valid token and store the user‑token relationship
tokenStore.storeAccessToken(existingAccessToken, authentication);
return existingAccessToken;
}
}
// No valid token exists – create a new one
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
// Store refresh token if one was generated
refreshToken = accessToken.getRefreshToken();
if (refreshToken != null) {
tokenStore.storeRefreshToken(refreshToken, authentication);
}
return accessToken;
}If a user already has a token, the service returns it instead of creating a new one; otherwise a fresh token (and optional refresh token) is generated and persisted.
Token Refresh Logic
curl --location --request POST 'http://auth-server/oauth/token?grant_type=refresh_token' \
--header 'Authorization: Basic dGVzdDp0ZXN0' \
--header 'VERSION: dev' \
--data-urlencode 'scope=server' \
--data-urlencode 'refresh_token=eccda61e-0c68-43af-8f67-6302cb389612'When a valid, unused refresh_token is presented, the authorization server invokes the RefreshTokenGranter to issue a new access token.
public class RefreshTokenGranter extends AbstractTokenGranter {
@Override
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
}
} @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
throws AuthenticationException {
createRefreshedAuthentication(authentication, tokenRequest);
if (!reuseRefreshToken) {
tokenStore.removeRefreshToken(refreshToken);
refreshToken = createRefreshToken(authentication);
}
OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
tokenStore.storeAccessToken(accessToken, authentication);
if (!reuseRefreshToken) {
tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
}
return accessToken;
}When Should the Client (Frontend) Refresh?
Passive Refresh
The client sends a request with an access token to a protected resource.
The resource server detects the token and forwards it to the auth server’s check_token endpoint.
If the token is expired, the auth server returns an error.
The client receives the error, then calls the auth server with its refresh_token.
The auth server issues a new access token, and the client retries the original request.
The drawback is that the user experiences a failed request before the token is refreshed.
Active Refresh
The client periodically calculates the remaining lifetime of its access token.
Before the token expires, it proactively requests a new token using the stored refresh_token.
Active refresh avoids request failures but consumes client resources to monitor token expiry.
// Example of a periodic token‑expiry check in a Vue component
refreshToken() {
this.refreshTime = setInterval(() => {
const token = getStore({ name: 'access_token', debug: true })
if (this.validatenull(token)) { return }
if (this.expires_in <= 1000 && !this.refreshLock) {
this.refreshLock = true
this.$store.dispatch('RefreshToken')
.catch(() => { clearInterval(this.refreshTime) })
this.refreshLock = false
}
this.$store.commit('SET_EXPIRES_IN', this.expires_in - 10)
}, 10000)
},Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
