What You’ll Build
A production-grade Token management layer — covering persistence storage selection, multi-user isolation, concurrent refresh race conditions, expiry degradation, proactive revocation, and secure storage. This ensures your SaaS application works correctly even after Token rotation and service restarts.Token Lifecycle
Core Principles
1. Always persist after refresh — TheonTokenRefresh callback is your only chance to save the new Token. Miss it, and the old Refresh Token is already invalidated while the new Token is lost — the user must re-authorize.
2. Never use in-memory-only storage — Service restart = all user Tokens lost = everyone must log in again. Tokens must be persisted to disk (DB / Redis / file).
3. Rotation must be atomic — After each refreshToken() call, the old Access Token and Refresh Token are both invalidated. Saving the new Token and discarding the old one must happen in the same transaction.
Step 1: Choose a Storage Strategy
Using Contrib Storage
Using Redis Custom Storage
Step 2: Multi-User Token Management
SaaS applications typically maintain independent Tokens for each user. The core pattern: use userId as the key, isolate reads and writes.Step 3: Concurrent Refresh Race Conditions
Problem: Two requests simultaneously discover the Access Token is expired and both attempt to refresh. The first succeeds, but the second receivesinvalid_grant because the Refresh Token has already been rotated.
Solution: Use a mutex to ensure only one refresh operation runs at a time per user.
The Python version re-checks if the Token was already refreshed by another coroutine after acquiring the lock (double-check pattern), avoiding redundant refreshes. The TypeScript version achieves the same effect by sharing a single Promise.
Map / dict locks aren’t sufficient. You need a distributed lock:
Step 4: Token Expiry Degradation
When the Refresh Token also expires (90 days without use) or is revoked, the SDK throwsAuthExpired. The only recovery path is guiding the user to re-authorize.
auth_expired response:
Frontend Degradation Component
Step 5: Token Revocation
When users proactively disconnect (revoke authorization) or you need to clean up unused Tokens, call the revocation API.- User clicks “Disconnect Profy” in your App settings
- User deletes their account
- Admin bulk-cleans inactive authorizations
- Abnormal access patterns detected
Step 6: Secure Storage
Encryption at Rest
Tokens are equivalent to user credentials and should be stored encrypted in the database:Security Checklist
Anti-Patterns
1. Storing Tokens in Frontend localStorage
2. Ignoring the onTokenRefresh Callback
3. Using an Already-Rotated Refresh Token
4. Not Handling Concurrent Refreshes
Production Checklist
Verify each item before going live:-
onTokenRefreshis configured and writes to persistent storage - Tokens are stored server-side (DB / Redis), not on the frontend
- Tokens are encrypted at the storage layer
- Concurrent refreshes are protected with a mutex
-
AuthExpiredhas degradation handling (guide to re-authorize) -
revokeToken()is called when users disconnect - Logs do not contain full Token values
- OAuth scopes follow least privilege
- Multi-instance deployments use distributed locks
- Encryption keys are not hardcoded in source code
Next Steps
Next.js Full-Stack Integration
Build a complete OAuth + billing Next.js application from scratch
Python FastAPI Integration
Python SDK + SQLAlchemy Token persistence
Webhook Event Handling
Receive and process real-time events pushed by the Profy platform
SDK Complete Guide
Dual-language API reference and Token persistence

