Information Security 4 min read

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.

Java Architecture Diary
Java Architecture Diary
Java Architecture Diary
Why OAuth2 Tokens Miss expires_in and How Spring Security Handles It

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.

<code>{
    "access_token":"16d35799-9cbb-4c23-966d-ab606029a623",
    "token_type":"bearer",
    "refresh_token":"495dbde5-1bbb-43c9-b06b-ecac50aa5d53",
    "expires_in":41000,
    "scope":"server"
}</code>

In the local deployment the response looks like:

<code>{
    "access_token":"c262afbe-441e-4023-afb4-f88c8a0a7d51",
    "token_type":"bearer",
    "refresh_token":"ea642d50-5cf5-48ad-9ef9-cb57c9dde00a",
    "scope":"server"
}</code>

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.

<code>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;
}</code>

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.

<code>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);
}</code>

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.

<code>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"
}</code>

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.

OAuth2Spring Securityaccess tokenexpires_intoken expiration
Java Architecture Diary
Written by

Java Architecture Diary

Committed to sharing original, high‑quality technical articles; no fluff or promotional content.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.