OAuth Token Manager

Difficulty: Hard · Topics: Design, Concurrency, API, Auth

Difficulty: Hard  |  Tags: API, Auth, Design, Concurrency

Description

Implement an OAuth 2.0 token manager that handles the full lifecycle: obtaining tokens, caching them, refreshing before expiry, and thread-safe concurrent access.

Implement:

OAuthTokenManager
  OAuthTokenManager(config)               — initialize with client credentials
  getAccessToken() → String               — return valid access token (auto-refresh)
  forceRefresh() → String                 — force a new token
  revokeToken()                            — revoke current token
  isTokenValid() → boolean                — check if current token is still valid

Config:

{
  clientId:     "your-client-id",
  clientSecret: "your-client-secret",
  tokenUrl:     "https://auth.example.com/oauth/token",
  refreshUrl:   "https://auth.example.com/oauth/refresh",
  grantType:    "client_credentials" | "authorization_code" | "refresh_token",
  scopes:       ["read", "write"]
}

Examples

Example 1 — Auto-refresh:

manager = new OAuthTokenManager(config)

token1 = manager.getAccessToken()    // Fetches new token from auth server
token2 = manager.getAccessToken()    // Returns cached token (still valid)

// ... token expires ...

token3 = manager.getAccessToken()    // Auto-refreshes, returns new token

Example 2 — Concurrent access (no duplicate requests):

// Token expired. 10 threads call getAccessToken() simultaneously:
// Only ONE thread actually calls the auth server.
// Other 9 threads wait and receive the same refreshed token.

Example 3 — Proactive refresh:

// Token expires at t=3600s
// Manager proactively refreshes at t=3300s (5 min before expiry)
// No request ever sees an expired token

Token Response (from auth server):

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2g...",
  "scope": "read write"
}

Constraints

Hints

Hint 1

Use a ReentrantLock or synchronized block around the refresh logic. Check the token again after acquiring the lock (double-checked locking).

Hint 2

Store expiresAt = System.currentTimeMillis() + (expiresIn * 1000) - bufferMs and check currentTime >= expiresAt for proactive refresh.

Follow-up

Practice this problem

Related problems