OAuth
token expiration
authentication
software development
API security

How to identify if the OAuth token has expired?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Knowing whether an OAuth access token has expired is important for both security and user experience. If you wait until every protected API call fails, your application becomes noisy and unpredictable. If you refresh too early, you add unnecessary traffic and complexity.

The correct approach depends on the kind of token you have. Some access tokens are self-contained JWTs, while others are opaque strings that only the authorization server can interpret.

Start by Understanding the Token Type

A common mistake is assuming every OAuth token is a JWT. That is not guaranteed by OAuth. Many providers issue JWT access tokens, but others issue opaque tokens that look like random strings.

This distinction matters because:

  • A JWT may contain an exp claim that tells you when it expires
  • An opaque token does not expose expiration data locally
  • Some providers support token introspection for opaque tokens
  • Some client applications should simply handle a 401 Unauthorized response and refresh

So the first question is not "how do I parse expiration?" It is "can this token be inspected safely on the client or server at all?"

Checking Expiration for JWT Access Tokens

If your provider issues JWTs, the token payload often contains the exp claim as a Unix timestamp. On a trusted backend, you should validate the token signature and then compare exp with the current time.

Here is a Python example using PyJWT:

python
1import time
2import jwt
3
4
5def is_expired(token: str, secret: str) -> bool:
6    payload = jwt.decode(
7        token,
8        secret,
9        algorithms=["HS256"],
10        options={"verify_aud": False},
11    )
12    return payload["exp"] <= time.time()

If you only need to read the timestamp for client-side refresh scheduling, you might decode without signature verification, but that should never be treated as an authorization decision. An unverified payload is useful for UX hints, not for trust.

python
1import time
2import jwt
3
4
5def expires_soon(token: str, seconds: int = 60) -> bool:
6    payload = jwt.decode(
7        token,
8        options={"verify_signature": False, "verify_exp": False},
9    )
10    return payload["exp"] <= time.time() + seconds

The extra buffer helps avoid race conditions where the token expires between the client check and the API request.

Handling Opaque Tokens

If the access token is opaque, you usually cannot determine expiration by inspecting the string. In that case, the usual options are:

  • Track the expiry time returned during token issuance
  • Call the provider’s introspection endpoint if supported
  • Retry with a refresh token when the API returns 401

Many OAuth token responses include expires_in, which tells you how many seconds the access token remains valid. If you store the token issuance time, you can compute a local expiry deadline without decoding anything.

python
1import time
2
3
4issued_at = time.time()
5expires_in = 3600
6
7
8def is_expired_from_metadata(now: float) -> bool:
9    return now >= issued_at + expires_in

This is often the simplest and most reliable method for non-JWT tokens.

Using Introspection on the Server

Some providers implement RFC 7662 token introspection. Your backend sends the token to the authorization server, and the server responds with metadata such as whether the token is active and when it expires.

python
1import requests
2
3
4def introspect_token(token: str) -> dict:
5    response = requests.post(
6        "https://auth.example.com/oauth/introspect",
7        data={"token": token},
8        auth=("client_id", "client_secret"),
9        timeout=5,
10    )
11    response.raise_for_status()
12    return response.json()

If the response says the token is inactive, treat it as expired or otherwise unusable. Introspection is especially useful when tokens can be revoked before their nominal expiry time.

The Most Practical Application Pattern

In production systems, a good strategy usually combines proactive and reactive checks:

  1. Store the expiry time when the token is issued.
  2. Refresh slightly before expiration, not exactly at the last second.
  3. Still handle 401 Unauthorized because clocks drift and tokens can be revoked.
  4. Keep refresh logic on the backend when possible.

That last point is important. Refresh tokens and client secrets should not be exposed to untrusted frontends. Browser applications often rely on the identity provider’s SDK or a backend-for-frontend pattern to avoid handling sensitive token flows directly.

Common Pitfalls

The first pitfall is treating every OAuth token as a JWT. If the token is opaque, local decoding will not tell you anything meaningful.

Another mistake is trusting an unverified JWT payload on the server. You can read exp without verification for convenience, but only signature-verified claims should influence security decisions.

Clock skew is also easy to miss. A token that looks valid on one machine may already be expired on another. Add a safety margin before expiry instead of using the exact timestamp as a hard edge.

Finally, do not assume expiration is the only reason a token stops working. Revocation, audience mismatch, missing scopes, or issuer changes can all produce failures that look similar at the API boundary.

Summary

  • The right expiration check depends on whether the access token is a JWT or an opaque token.
  • For JWTs, validate the token and compare the exp claim to the current time.
  • For opaque tokens, rely on expires_in, token introspection, or 401 handling.
  • Add a refresh buffer to avoid race conditions near the expiration boundary.
  • Expiration checks improve reliability, but API error handling is still required in real systems.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.