formreader-session-timeout
v0.2.6
Published
Session timeout microfrontend: configurable JWT expiry decoding and refresh with idle tracking
Readme
@formreader/session-timeout
A lightweight, fully configurable React microfrontend for JWT token refresh management, idle session tracking, and auto-logout with comprehensive event callbacks.
Features
- ✅ JWT Expiry Decoding — Reads token expiry from JWT
expclaim, no server roundtrip needed - ✅ Configurable Refresh Timing — Schedule token refresh at any point before expiry
- ✅ Idle Detection — Automatic idle timeout with configurable warning dialogs
- ✅ Custom Payload Formatting — Tailor refresh/logout request payloads to your API
- ✅ Flexible HTTP Integration — Use your own axios/fetch client or rely on built-in fetch
- ✅ Event-Driven — Full control via callbacks:
onRefreshSuccess,onIdle,onSessionExpired, etc. - ✅ Zero External Dependencies — Works standalone; React is a peer dependency only
- ✅ Fully Typed — TypeScript support out of the box
- ✅ Singleton Pattern — Global session manager or per-component hooks
Installation
npm install @formreader/session-timeoutOr using yarn:
yarn add @formreader/session-timeoutQuick Start
1. Hook-Based Usage (Recommended)
Import and use the useSessionTimeout hook in any React component:
import React from 'react';
import { useSessionTimeout } from '@formreader/session-timeout';
export function MyComponent() {
const {
sessionState,
timeUntilIdle,
idleWarningVisible,
extendSession,
refreshToken,
logout,
} = useSessionTimeout({
enabled: true,
config: {
refreshThresholdMs: 2 * 60 * 1000, // Refresh 2 min before expiry
idleTimeoutMs: 15 * 60 * 1000, // Logout after 15 min idle
idleCheckIntervalMs: 10 * 1000, // Check idle every 10 sec
idleWarningThresholdMs: 2 * 60 * 1000, // Show warning 2 min before idle
autoRefresh: true,
showIdleWarning: true,
debug: true,
// Your API endpoints
refreshEndpoint: '/api/auth/refresh/',
logoutEndpoint: '/api/auth/logout/',
// Custom payload format (if needed)
refreshPayloadFormatter: (token) => ({ refresh: token }),
},
onRefreshSuccess: () => console.log('Token refreshed'),
onIdle: () => console.log('User idle'),
onSessionExpired: () => console.log('Session expired'),
});
return (
<div>
<p>Session: {sessionState.isActive ? '🟢 Active' : '🔴 Inactive'}</p>
{idleWarningVisible && (
<div className="warning">
<p>Your session is about to expire in {timeUntilIdle}ms</p>
<button onClick={extendSession}>Stay Logged In</button>
</div>
)}
</div>
);
}2. Service-Based Usage
Use SessionManager directly for more control:
import {
getSessionManager,
resetSessionManager,
getStoredToken,
getTokenInfo,
} from '@formreader/session-timeout';
// Initialize on app startup
const manager = getSessionManager({
refreshThresholdMs: 2 * 60 * 1000,
idleTimeoutMs: 15 * 60 * 1000,
autoRefresh: true,
debug: true,
refreshEndpoint: '/api/auth/refresh/',
logoutEndpoint: '/api/auth/logout/',
refreshPayloadFormatter: (token) => ({ refresh: token }),
});
manager.init();
// Listen to events
manager.on('tokenRefreshed', () => console.log('Token refreshed'));
manager.on('idle', () => console.log('User idle'));
// Manual refresh if needed
await manager.refreshToken();
// Cleanup on logout or unmount
manager.destroy();Configuration
All settings are optional and inherit sensible defaults. Pass them via the config object:
Session Timing
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| refreshThresholdMs | number | 2 * 60 * 1000 | Milliseconds before token expiry to trigger refresh |
| idleTimeoutMs | number | 15 * 60 * 1000 | Milliseconds of inactivity before session ends |
| idleCheckIntervalMs | number | 10 * 1000 | How often to check for idle activity |
| idleWarningThresholdMs | number | 2 * 60 * 1000 | Milliseconds before idle timeout to show warning |
| maxSessionDurationMs | number | 8 * 60 * 60 * 1000 | Absolute max session duration (8 hours) |
API Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| refreshEndpoint | string | /auth/refresh/ | URL for token refresh |
| logoutEndpoint | string | /auth/logout/ | URL for logout |
| autoRefresh | boolean | true | Auto-refresh tokens before expiry |
UI Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| showIdleWarning | boolean | true | Show idle warning dialog |
| debug | boolean | false | Log debug messages to console |
Custom Integration
| Option | Type | Description |
|--------|------|-------------|
| httpClient? | { post(...): Promise } | Custom HTTP client (defaults to fetch) |
| refreshPayloadFormatter? | (token) => object | Format refresh request payload |
| logoutPayloadFormatter? | (token) => object | Format logout request payload |
Custom HTTP Client
If you have an existing axios instance or custom HTTP client, pass it to avoid dependencies:
import axios from 'axios';
const { useSessionTimeout } = require('@formreader/session-timeout');
useSessionTimeout({
config: {
httpClient: {
post: (url, body, options) => axios.post(url, body, options),
},
refreshThresholdMs: 2 * 60 * 1000,
// ... other config
},
});Custom Payload Formatting
Your API may expect a different payload structure. Use the formatters to customize:
Example 1: API requires refresh field
useSessionTimeout({
config: {
refreshPayloadFormatter: (token) => ({
refresh: token, // API expects "refresh", not "token"
}),
logoutPayloadFormatter: (token) => ({
refresh: token,
}),
},
});Request sent:
POST /api/auth/refresh/
{ "refresh": "eyJhbGc..." }Example 2: Complex nested structure
useSessionTimeout({
config: {
refreshPayloadFormatter: (token) => ({
action: 'refresh',
credentials: {
refresh_token: token,
client_id: 'my-client',
},
}),
},
});Request sent:
POST /api/auth/refresh/
{
"action": "refresh",
"credentials": {
"refresh_token": "eyJhbGc...",
"client_id": "my-client"
}
}Event Callbacks
Full control over session lifecycle:
useSessionTimeout({
config: { /* ... */ },
// Called when token is about to expire
onSessionExpiring: () => {
console.warn('Token expiring soon');
},
// Called when user becomes idle
onIdle: () => {
console.log('User idle – logging out');
},
// Called when session has completely expired
onSessionExpired: () => {
console.log('Session expired – redirecting to login');
window.location.href = '/login';
},
// Called after successful token refresh
onRefreshSuccess: () => {
console.log('Token refreshed silently');
},
// Called on refresh failure
onRefreshFailure: (error) => {
console.error('Refresh failed:', error.message);
},
});Token Storage
Tokens are stored in sessionStorage by default (cleared on browser close). To persist across tabs, use localStorage:
import { storeToken, getStoredToken } from '@formreader/session-timeout';
// Store persistently
storeToken(token, true); // true = use localStorage
// Retrieve (checks both storage types)
const token = getStoredToken();Token Inspection
Decode and inspect JWT payloads without verification (client-side only):
import { getTokenInfo, isTokenExpired, getTimeUntilExpiry } from '@formreader/session-timeout';
const token = getStoredToken();
// Get full token info
const info = getTokenInfo(token);
console.log(info.expiresAt); // Unix timestamp (ms)
console.log(info.expiresIn); // Milliseconds until expiry
console.log(info.payload); // JWT payload object
// Quick checks
const expired = isTokenExpired(token);
const timeLeft = getTimeUntilExpiry(token);Global Session Manager
For app-wide state, initialize the singleton once at startup:
import { getSessionManager, resetSessionManager } from '@formreader/session-timeout';
// App startup
const manager = getSessionManager({
refreshThresholdMs: 2 * 60 * 1000,
idleTimeoutMs: 15 * 60 * 1000,
autoRefresh: true,
refreshEndpoint: '/api/auth/refresh/',
logoutEndpoint: '/api/auth/logout/',
});
manager.init();
// Listen to events
manager.on('tokenRefreshed', () => console.log('Token refreshed'));
// Later, reset if needed (e.g., on logout)
resetSessionManager();TypeScript Support
Full TypeScript definitions included:
import {
SessionConfig,
SessionState,
TokenInfo,
JWTPayload,
RefreshTokenResponse,
UseSessionTimeoutOptions,
} from '@formreader/session-timeout';
const config: SessionConfig = {
refreshThresholdMs: 2 * 60 * 1000,
idleTimeoutMs: 15 * 60 * 1000,
// ... rest of config
};API Reference
useSessionTimeout(options?)
React hook for session management in components.
Parameters:
options.enabled?: boolean— Enable/disable the hook (default:true)options.config?: Partial<SessionConfig>— Configuration (merged with defaults)options.onSessionExpiring?: () => void— Callback before expiryoptions.onSessionExpired?: () => void— Callback on expiryoptions.onIdle?: () => void— Callback on idleoptions.onRefreshSuccess?: () => void— Callback on refresh successoptions.onRefreshFailure?: (error: Error) => void— Callback on refresh failure
Returns:
{
sessionState: SessionState; // Current session state
timeUntilIdle: number | null; // Milliseconds until idle
idleWarningVisible: boolean; // Show idle warning
extendSession: () => void; // Reset idle timer
refreshToken: () => Promise<boolean>; // Manually refresh
logout: () => Promise<void>; // Logout
updateConfig: (newConfig) => void; // Update config
manager: SessionManager; // Underlying manager
}SessionManager
Direct access to session management service.
Methods:
init()— Initialize session managementrefreshToken(): Promise<boolean>— Refresh tokenlogout(): Promise<void>— Logout and cleanupextendSession()— Reset idle timergetState(): SessionState— Current stategetConfig(): SessionConfig— Current configupdateConfig(newConfig)— Update configon(event, callback): () => void— Subscribe to eventsdestroy()— Cleanup and destroy manager
Events:
'initialized'— Manager initialized'tokenRefreshed'— Token successfully refreshed'idle'— User became idle'activity'— User activity detected'idleWarning'— Idle warning (includestimeRemaining)'sessionExtended'— Session extended'refreshFailed'— Token refresh failed'loggedOut'— Logged out
Token Service Functions
getTokenInfo(token): TokenInfo | null— Get token expiry and payloadisTokenExpired(token, bufferMs?): boolean— Check if token expiredgetTimeUntilExpiry(token): number— Get milliseconds until expirygetStoredToken(): string | null— Get token from storagestoreToken(token, persistent?)— Store tokenclearToken()— Clear stored tokenvalidateToken(token)— Validate token structure
Production Checklist
- [ ] Configure
refreshThresholdMsfor your token lifetime - [ ] Set
refreshEndpointto your backend refresh URL - [ ] Customize
refreshPayloadFormatterif your API requires custom payload - [ ] Set appropriate
idleTimeoutMsbased on security requirements - [ ] Pass your app's HTTP client via
httpClientoption - [ ] Test token refresh before token expires
- [ ] Test idle timeout and warning
- [ ] Handle
onSessionExpiredto redirect to login - [ ] Disable
debug: truein production - [ ] Monitor
onRefreshFailurefor refresh failures
Browser Support
- Chrome/Edge: ✅ All versions
- Firefox: ✅ All versions
- Safari: ✅ 12+
- Mobile: ✅ iOS 12+, Android 5+
Requires fetch API or polyfill.
License
MIT
