Why OAuth2 Tokens Miss expires_in and How Spring Security Handles It
The article examines why the demo environment of pig4cloud returns an OAuth2 access token without the expires_in field, contrasts it with a local deployment, analyzes the Spring Security OAuth2 token generation code, and explains that according to the OAuth2 specification the expires_in parameter should be returned even for permanently valid tokens.
Problem Background
Someone asked why the demo environment of pig4cloud returns a login response without the expires_in field, while a locally deployed instance includes it.
{
"access_token":"16d35799-9cbb-4c23-966d-ab606029a623",
"token_type":"bearer",
"refresh_token":"495dbde5-1bbb-43c9-b06b-ecac50aa5d53",
"expires_in":41000,
"scope":"server"
}In the local deployment the response looks like:
{
"access_token":"c262afbe-441e-4023-afb4-f88c8a0a7d51",
"token_type":"bearer",
"refresh_token":"ea642d50-5cf5-48ad-9ef9-cb57c9dde00a",
"scope":"server"
}The missing expires_in parameter means the client cannot know when to refresh the token.
Source Code Analysis
In Spring Security OAuth2 the token creation method adds the expires_in field only when the configured token validity (validSeconds) is greater than zero.
OAuth2AccessToken createAccessToken() {
DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
if (validitySeconds > 0) {
token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
}
token.setRefreshToken(refreshToken);
token.setScope(authentication.getOAuth2Request().getScope());
return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
}When the token store saves a token with an expiration of zero or negative, the expiration is null, meaning the token is considered permanently valid, and the expires_in field is omitted in the response.
if (token.getExpiration() != null) {
int seconds = token.getExpiresIn();
conn.expire(accessKey, seconds);
conn.expire(authKey, seconds);
conn.expire(authToAccessKey, seconds);
conn.expire(clientId, seconds);
conn.expire(approvalKey, seconds);
}Should a permanently valid token return expires_in?
According to the OAuth2 specification, the expires_in field is recommended to be returned regardless of whether the token has a limited lifetime.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope":"create"
}access_token (required): the token issued by the authorization server.
token_type (required): typically the string "bearer".
expires_in (recommended): lifetime of the access token in seconds.
refresh_token (optional): token used to obtain a new access token after expiration.
scope (optional): the granted scope.
Therefore, Spring Security OAuth2’s omission of expires_in for permanently valid tokens does not conform to the specification.
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.
