cognito-session-manager
v0.1.1
Published
Lightweight, framework-agnostic session lifecycle management for Amazon Cognito User Pools
Maintainers
Readme
Amazon Cognito Session Manager
Lightweight, framework-agnostic session lifecycle management for Amazon Cognito User Pools. Provides automatic token refresh, multi-tab synchronization, and pluggable storage without requiring AWS Amplify.
Overview
When you use Amazon Cognito User Pools with the AWS SDK directly, you receive tokens but must manage their lifecycle yourself. This includes tracking expiration, refreshing tokens before they expire, handling concurrent refresh requests, and coordinating sessions across browser tabs.
cognito-session-manager solves these problems in a single, focused library:
- 2.8 KB gzipped core + 0.5 KB fetch interceptor
- Zero runtime dependencies (peer dependency on
@aws-sdk/client-cognito-identity-provider) - Works everywhere - Node.js 18+, modern browsers, React Native, Deno, Cloudflare Workers
- No framework lock-in - use with React, Vue, Angular, Svelte, or vanilla JavaScript
Installation
npm install cognito-session-manager @aws-sdk/client-cognito-identity-providerGetting Started
Basic Usage
import { SessionManager } from 'cognito-session-manager';
const session = new SessionManager({
region: 'us-east-1',
clientId: 'your-app-client-id',
});
// After authenticating your user through any method (hosted UI, SRP, custom flow),
// store the resulting tokens:
await session.setTokens({
accessToken: authResult.AccessToken,
idToken: authResult.IdToken,
refreshToken: authResult.RefreshToken,
expiresAt: Math.floor(Date.now() / 1000) + authResult.ExpiresIn,
clockDrift: 0,
});
// Retrieve a valid access token. The library refreshes automatically
// if the token is about to expire.
const token = await session.getAccessToken();Restoring Sessions on App Startup
import { SessionManager, LocalStorageAdapter } from 'cognito-session-manager';
const session = new SessionManager({
region: 'us-east-1',
clientId: 'your-app-client-id',
storage: new LocalStorageAdapter(),
});
// Loads tokens from storage. If expired, attempts a refresh automatically.
const tokens = await session.initialize();
if (!tokens) {
// No valid session found, redirect to login
redirectToLogin();
}Listening to Session Events
const unsubscribe = session.on((event) => {
switch (event.type) {
case 'signedIn':
console.log('User signed in');
break;
case 'tokenRefreshed':
console.log('Tokens refreshed automatically');
break;
case 'signedOut':
console.log('User signed out');
break;
case 'sessionExpired':
console.log('Session expired:', event.reason);
redirectToLogin();
break;
}
});
// Later, to stop listening:
unsubscribe();Fetch Interceptor
The library includes a separate entry point for automatically attaching access tokens to HTTP requests:
import { SessionManager } from 'cognito-session-manager';
import { createAuthFetch } from 'cognito-session-manager/fetch';
const session = new SessionManager({ region: 'us-east-1', clientId: '...' });
const authFetch = createAuthFetch(session);
// The token is attached as "Authorization: Bearer <token>" automatically.
// On a 401 response, the library refreshes the token and retries once.
const response = await authFetch('/api/protected-resource');Fetch Interceptor Options
const authFetch = createAuthFetch(session, {
// Only attach the token to requests matching this filter
shouldAttachToken: (url) => url.startsWith('/api'),
// Use a custom header name (default: 'Authorization')
headerName: 'X-Auth-Token',
// Use a custom header value format (default: 'Bearer {token}')
headerValue: (token) => token,
// Disable automatic retry on 401 (default: true)
retryOnUnauthorized: false,
});Storage Adapters
By default, tokens are stored in memory and lost on page reload. The library ships with three storage adapters:
MemoryStorage (default)
import { SessionManager, MemoryStorage } from 'cognito-session-manager';
const session = new SessionManager({
region: 'us-east-1',
clientId: '...',
storage: new MemoryStorage(),
});LocalStorageAdapter
Persists tokens in the browser's localStorage:
import { SessionManager, LocalStorageAdapter } from 'cognito-session-manager';
const session = new SessionManager({
region: 'us-east-1',
clientId: '...',
storage: new LocalStorageAdapter(), // default prefix: 'cognito-session:'
// storage: new LocalStorageAdapter('myapp:') // custom prefix
});CookieStorageAdapter
Persists tokens in cookies. Useful for SSR or cross-subdomain scenarios:
import { SessionManager, CookieStorageAdapter } from 'cognito-session-manager';
const session = new SessionManager({
region: 'us-east-1',
clientId: '...',
storage: new CookieStorageAdapter({
domain: '.example.com',
secure: true,
sameSite: 'Lax',
maxAge: 30 * 24 * 60 * 60, // 30 days
}),
});Custom Storage Adapter
Implement the StorageAdapter interface to use any persistence layer:
import type { StorageAdapter } from 'cognito-session-manager';
const redisAdapter: StorageAdapter = {
async getItem(key: string): Promise<string | null> {
return redis.get(key);
},
async setItem(key: string, value: string): Promise<void> {
await redis.set(key, value);
},
async removeItem(key: string): Promise<void> {
await redis.del(key);
},
};Multi-Tab Synchronization
In browser environments, the library automatically synchronizes session state across tabs using the BroadcastChannel API:
- When one tab refreshes tokens, all other tabs receive the updated tokens.
- When one tab signs out, all other tabs are notified immediately.
- Only one tab performs the actual refresh network call. Other tabs wait for the broadcast.
This behavior is enabled by default in environments where BroadcastChannel is available. You can disable it:
const session = new SessionManager({
region: 'us-east-1',
clientId: '...',
enableTabSync: false,
});API Reference
SessionManager
Constructor
new SessionManager(config: SessionManagerConfig)Configuration
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| region | string | Yes | - | AWS Region of your Cognito User Pool |
| clientId | string | Yes | - | The App Client ID from your User Pool |
| userPoolId | string | No | - | The User Pool ID (reserved for future use) |
| storage | StorageAdapter | No | MemoryStorage | Storage adapter for token persistence |
| refreshBufferSeconds | number | No | 60 | How many seconds before expiry to trigger a proactive refresh |
| enableTabSync | boolean | No | auto | Whether to synchronize sessions across browser tabs |
| cognitoClient | CognitoIdentityProviderClient | No | auto | Provide your own configured SDK client |
Methods
| Method | Returns | Description |
|--------|---------|-------------|
| initialize() | Promise<AuthTokens \| null> | Restores a session from storage. Call once on app startup. Attempts refresh if tokens are expired. |
| setTokens(tokens) | Promise<void> | Stores tokens after a successful authentication. Starts the refresh scheduler. |
| getAccessToken() | Promise<string> | Returns the current access token. Triggers a refresh if the token is about to expire. |
| getIdToken() | Promise<string> | Returns the current ID token. |
| getTokens() | Promise<AuthTokens \| null> | Returns all current tokens, or null if no session exists. |
| forceRefresh() | Promise<AuthTokens> | Forces an immediate token refresh regardless of expiry status. |
| signOut() | Promise<void> | Clears tokens, cancels the refresh scheduler, and notifies other tabs. |
| on(handler) | () => void | Subscribes to session events. Returns an unsubscribe function. |
| destroy() | void | Cleans up timers, BroadcastChannel listeners, and event subscriptions. |
Types
interface AuthTokens {
accessToken: string;
idToken: string;
refreshToken: string;
expiresAt: number; // Unix timestamp in seconds
clockDrift: number; // Detected clock drift in seconds
}
type SessionEvent =
| { type: 'signedIn'; tokens: AuthTokens }
| { type: 'tokenRefreshed'; tokens: AuthTokens }
| { type: 'signedOut' }
| { type: 'sessionExpired'; reason: 'refreshFailed' | 'refreshTokenExpired' }
| { type: 'error'; error: Error };How It Works
Proactive Token Refresh
The library schedules a refresh refreshBufferSeconds before the access token expires (default: 60 seconds). This means requests never encounter a 401 due to token expiry under normal network conditions.
Concurrency Deduplication
If multiple parts of your application call getAccessToken() or forceRefresh() simultaneously, only one network request is made to Cognito. All concurrent callers receive the same result from the single request.
Clock Drift Detection
On each token refresh, the library reads the iat (issued at) claim from the JWT and compares it to the local system clock. The detected drift is stored and applied to all future expiry calculations, preventing premature or delayed refreshes on devices with inaccurate clocks.
Error Recovery
| Scenario | Behavior |
|----------|----------|
| Network error during refresh | Retries once after 5 seconds, then emits sessionExpired with reason refreshFailed |
| Refresh token revoked or expired | Emits sessionExpired with reason refreshTokenExpired |
| Storage adapter throws on write | Tokens remain in memory; no data loss occurs |
| BroadcastChannel unavailable | Gracefully degrades; no multi-tab sync |
Scope and Limitations
This library manages sessions after authentication. It does not:
- Provide sign-in or sign-up UI components
- Handle the initial authentication flow (hosted UI, SRP, custom auth challenges)
- Manage Amazon Cognito Identity Pool (federated identity) credentials
- Replace the full AWS Amplify Auth module
Use it alongside your existing authentication flow. Authenticate with whatever method you prefer, then pass the resulting tokens to setTokens().
Interactive Demo
An interactive dashboard that demonstrates all library features against a real Cognito User Pool is available at:
The demo lets you:
- Sign in and observe live token state (expiry countdown, clock drift, refresh count)
- Fire concurrent refresh calls and verify deduplication (5 calls, 1 API request)
- Test the fetch interceptor against a live endpoint
- Open multiple tabs to observe BroadcastChannel synchronization
- Compare the library's behavior side-by-side with the raw SDK approach
Development
git clone https://github.com/paramanandmallik/cognito-session-manager.git
cd cognito-session-manager
npm install
npm run build # Builds ESM, CJS, and type declarations with tsup
npm run test # Runs the full test suite (79 tests)
npm run typecheck # Runs tsc with no outputSecurity
If you discover a potential security issue in this project, please do not create a public GitHub issue. Instead, contact the maintainer directly.
License
This project is licensed under the MIT License.
