Users may experience abrupt session terminations and forced logouts during system operations, often due to expired authentication tokens. This issue persists even when Redis caches user IDs and token data. The core problem lies in token expiration invalidating user identity.
Automatic token refresh mechanisms provide solutions by generating new tokens before expiration. This can be implemented through proactive token renewal or scheduled refresh tasks that extend validity periods without creating entirely new tokens.
Server-Side Token Refresh Implementation
Backend systems can monitor token expiration times and automatically issue new tokens when approaching expiry. The renewed token is embedded in response headers, allowing client-side interceptors to detect and replace stored tokens when discrepancies occur.
Required dependencies for Java implementation:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
Token generation utility class:
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.time.Instant;
import java.util.Date;
import java.util.UUID;
public class TokenManager {
private static final long DEFAULT_VALIDITY = 86400000L; // 24 hours
private static final String SECRET = "application-secret-key";
private static SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(SECRET.getBytes());
}
public static String generateToken(String payload) {
return buildToken(payload, DEFAULT_VALIDITY, UUID.randomUUID().toString());
}
public static String generateToken(String payload, long validityMillis) {
return buildToken(payload, validityMillis, UUID.randomUUID().toString());
}
private static String buildToken(String subject, long ttlMillis, String identifier) {
Instant now = Instant.now();
Instant expiration = now.plusMillis(ttlMillis);
return Jwts.builder()
.setId(identifier)
.setSubject(subject)
.setIssuer("auth-service")
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(expiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
public static Instant getExpirationTime(String token) {
return parseToken(token).getExpiration().toInstant();
}
}
Expiration validation logic:
public class TokenValidator {
private static final long REFRESH_THRESHOLD = 300000L; // 5 minutes
public static boolean requiresRefresh(String token) {
try {
Instant expiration = TokenManager.getExpirationTime(token);
Instant currentTime = Instant.now();
long remainingMillis = expiration.toEpochMilli() - currentTime.toEpochMilli();
return remainingMillis < REFRESH_THRESHOLD;
} catch (JwtException e) {
return false;
}
}
}
During authentication verification, when a tokan passes validation but approaches expiry, the backend generates a replacement token using original claims data. This new token is attached to response headers for client-side retrieval and storage updates.
Client-Side Token Renewal Strategies
Frontend implementations typically employ dual-token architecture with access tokens (AT) and refresh tokens (RT). AT tokens have shorter lifespans for reduced security exposure during frequent API requests, while RT tokens maintain longer validity for renewal operations.
When frontend monitoring detects approaching AT expiration, it sends the RT to dedicated renewal endpoints. The backend validates the RT and issues fresh AT tokens, extending session continuity without user intervention.
Handling Inactive Session Scenarios
Extended user inactivity during form completion presents challenges when tokens expire without backend interaction. Two approaches address this:
For server-centric implementations, frontends should implemant local draft saving mechanisms. When submission requests return 401 responses, forms can be preserved in local storage, followed by authentication redirects and data restoration upon successful login.
Client-side solutions require monitoring both AT and RT expiration times. Proactive refresh requests should be scheduled before RT expiration, combined with automatic form persistence similar to draft functionality.