@sauravart/fgs-session-management
v1.1.4
Published
Angular session management library with timeout handling, activity tracking, multi-session conflict detection, cross-tab synchronization, network status handling, session lock/unlock, and UI components
Maintainers
Readme
FGS Session Management
A comprehensive Angular session management library with timeout handling, activity tracking, multi-session conflict detection, cross-tab synchronization, network status handling, session lock/unlock, and beautiful UI components.
Features
- Session Timeout Management - Automatic session expiry with configurable timeout and warning countdown
- Activity Tracking - Track user activity (mouse, keyboard, scroll, touch, visibility)
- Warning Dialog - Beautiful countdown dialog before session expires
- Multi-Session Detection - Detect and handle multiple active sessions across devices
- Cross-Tab Synchronization - Sync session state across browser tabs using BroadcastChannel API
- Network Status Handling - Detect online/offline status and handle reconnection gracefully
- Session Lock/Unlock - Lock sessions for security with optional password protection
- Proactive Session Refresh - Automatically refresh sessions before expiry
- Error Recovery - Automatic retry with exponential backoff for session creation
- Route Guards - Protect routes with authentication, role, and permission guards
- HTTP Interceptor - Automatic token injection and 401/403 handling
- Device Fingerprinting - Track sessions with unique device identification
- IP Geolocation - Optional IP-based location tracking
- Fully Customizable - Override UI components, callbacks, and behavior
Installation
npm install @sauravart/fgs-session-managementRequirements
- Angular 17, 18, or 19
- RxJS 7.x
Quick Start
1. Configure the Provider
In your app.config.ts:
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideFgsSessionWithInterceptor } from '@sauravart/fgs-session-management';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideFgsSessionWithInterceptor({
apiBaseUrl: 'http://localhost:3000/api', // Your API base URL
endpoints: {
createSession: '/session/create',
validateSession: '/session/validate',
refreshSession: '/session/refresh',
revokeSession: '/session/revoke',
listSessions: '/session/list',
logout: '/auth/logout',
},
sessionTimeoutMs: 300000, // 5 minutes (should match API config)
warningWindowSeconds: 60, // Show warning 60 seconds before expiry
}),
],
};2. Add UI Components to App Component
In your app.component.ts:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {
FgsSessionTimeoutDialogComponent,
FgsSessionConflictModalComponent,
FgsSessionLockScreenComponent,
} from '@sauravart/fgs-session-management';
@Component({
selector: 'app-root',
standalone: true,
imports: [
RouterOutlet,
FgsSessionTimeoutDialogComponent,
FgsSessionConflictModalComponent,
FgsSessionLockScreenComponent,
],
template: `
<router-outlet></router-outlet>
<!-- Session Management UI Components -->
<fgs-session-timeout-dialog></fgs-session-timeout-dialog>
<fgs-session-conflict-modal></fgs-session-conflict-modal>
<fgs-session-lock-screen></fgs-session-lock-screen>
`,
})
export class AppComponent {}3. Initialize Session After Login
In your login component or service:
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
import { FgsSessionService } from '@sauravart/fgs-session-management';
@Component({
selector: 'app-login',
// ...
})
export class LoginComponent {
private sessionService = inject(FgsSessionService);
private router = inject(Router);
async onLoginSuccess(response: any) {
try {
// Initialize session after successful login
await this.sessionService.initializeSession(
response.userData, // User data object
response.authToken, // Authentication token
response.userId, // Optional: User ID
response.userCode // Optional: User code
);
// Navigate to dashboard
this.router.navigate(['/dashboard']);
} catch (error) {
console.error('Failed to initialize session:', error);
}
}
}4. Protect Routes with Guards
import { Routes } from '@angular/router';
import {
fgsSessionGuard,
fgsGuestGuard,
fgsRoleGuard,
fgsPermissionGuard,
} from '@sauravart/fgs-session-management';
export const routes: Routes = [
// Public routes (only accessible when NOT logged in)
{
path: 'login',
component: LoginComponent,
canActivate: [fgsGuestGuard],
},
// Protected routes (require authentication)
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [fgsSessionGuard],
},
// Role-based protection
{
path: 'admin',
component: AdminComponent,
canActivate: [fgsSessionGuard, fgsRoleGuard(['admin', 'superadmin'])],
},
// Permission-based protection
{
path: 'reports',
component: ReportsComponent,
canActivate: [fgsSessionGuard, fgsPermissionGuard(['view_reports'])],
},
// Permission-based with ALL permissions required
{
path: 'settings',
component: SettingsComponent,
canActivate: [fgsSessionGuard, fgsPermissionGuard(['read_settings', 'write_settings'], true)],
},
];Configuration Options
Full Configuration Example
provideFgsSession({
// Required: API Configuration
apiBaseUrl: 'https://your-api.com/api',
endpoints: {
createSession: '/session/create',
validateSession: '/session/validate',
refreshSession: '/session/refresh',
revokeSession: '/session/revoke',
listSessions: '/session/list',
logout: '/auth/logout',
},
// Timing Configuration
sessionTimeoutMs: 600000, // 10 minutes (default)
warningWindowSeconds: 60, // 60 seconds (default)
activityThrottleMs: 1000, // 1 second (default)
refreshThrottleMs: 30000, // 30 seconds (default)
validationThrottleMs: 5000, // 5 seconds (default)
// Feature Flags
features: {
enableMultiSessionDetection: true, // Detect multiple active sessions
enableDeviceFingerprint: true, // Generate unique device ID
enableIPGeolocation: true, // Fetch IP-based location
enableActivityTracking: true, // Track user activity
},
// UI Configuration
ui: {
showTimeoutDialog: true, // Show timeout warning dialog
showConflictModal: true, // Show conflict detection modal
showLockScreen: true, // Show lock screen when locked
dialogTitle: 'Session Expiring',
dialogMessage: 'Your session is about to expire due to inactivity.',
dialogExtendButtonText: 'Extend Session',
dialogLogoutButtonText: 'Logout',
conflictModalTitle: 'Active Session Detected',
conflictModalMessage: 'Another session is already active.',
forceLogoutDelaySeconds: 300, // 5 minutes before force logout enabled
lockScreenTitle: 'Session Locked',
lockScreenMessage: 'Your session has been locked for security.',
},
// Route Configuration
routes: {
loginRoute: '/login',
afterLoginRoute: '/dashboard',
},
// Storage Keys (customize if needed)
storageKeys: {
authToken: 'Authtoken',
sessionId: 'sessionId',
sessionAuthToken: 'sessionAuthToken',
userData: 'localUserData',
lastApiCallTime: 'lastApiCallTime',
lastActivity: 'userLastActivity',
deviceId: 'deviceId',
},
// Cookie Configuration
cookieConfig: {
name: 'Authtoken',
secure: true,
sameSite: 'strict',
path: '/',
},
// Geolocation Configuration
geolocation: {
apiUrl: 'https://ip-api.com/json',
timeout: 5000,
},
// Callbacks
callbacks: {
onSessionCreated: (session) => console.log('Session created:', session),
onSessionExpired: () => console.log('Session expired'),
onSessionExtended: () => console.log('Session extended'),
onForceLogout: () => console.log('Force logout'),
onConflictDetected: (sessions) => console.log('Conflict:', sessions),
onLogout: () => console.log('Logged out'),
onUnauthorized: () => console.log('Unauthorized'),
},
// HTTP Interceptor Configuration
useLegacyAuthHeader: false, // Don't send legacy Authtoken header (default: false)
interceptorSkipUrls: ['/public'], // Additional URLs to skip
customHeaders: { // Custom headers for all requests
'X-App-Version': '1.0.0',
},
// Debug Mode
debug: false,
})Services
FgsSessionService
The main service for session management.
import { Component, inject, OnInit } from '@angular/core';
import { FgsSessionService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent implements OnInit {
private sessionService = inject(FgsSessionService);
ngOnInit() {
// Subscribe to session events
this.sessionService.sessionCreated$.subscribe((session) => {
console.log('Session created:', session);
});
this.sessionService.sessionExpired$.subscribe(() => {
console.log('Session expired!');
});
this.sessionService.sessionLocked$.subscribe(() => {
console.log('Session locked!');
});
this.sessionService.sessionUnlocked$.subscribe(() => {
console.log('Session unlocked!');
});
this.sessionService.networkStatusChanged$.subscribe((isOnline) => {
console.log('Network status:', isOnline ? 'Online' : 'Offline');
});
this.sessionService.conflictDetected$.subscribe((sessions) => {
console.log('Conflict detected:', sessions);
});
}
// Initialize session after login
async initializeSession(userData: any, authToken: string) {
await this.sessionService.initializeSession(userData, authToken);
}
// Extend session
extendSession() {
this.sessionService.extendSession().subscribe();
}
// Logout
logout() {
this.sessionService.logout().subscribe();
}
// Lock session
lockSession() {
this.sessionService.lockSession();
}
// Unlock session
unlockSession() {
this.sessionService.unlockSession();
}
// Check if session is locked
get isLocked() {
return this.sessionService.getIsLocked();
}
// Check authentication state (computed signal)
get isAuthenticated() {
return this.sessionService.isAuthenticated();
}
// Get current session ID (computed signal)
get sessionId() {
return this.sessionService.currentSessionId();
}
// Get current user ID (computed signal)
get userId() {
return this.sessionService.currentUserId();
}
// Get full session state
getSessionState() {
return this.sessionService.getSessionState();
}
// Check if session is valid
isSessionValid() {
return this.sessionService.isSessionValid();
}
}FgsSessionTimerService
Manage session timer directly.
import { Component, inject, OnInit } from '@angular/core';
import { FgsSessionTimerService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent implements OnInit {
private timerService = inject(FgsSessionTimerService);
ngOnInit() {
// Subscribe to timer trigger (seconds remaining in warning window)
this.timerService.timerTrigger$.subscribe((seconds) => {
if (seconds > 0) {
console.log(`Warning: ${seconds} seconds remaining`);
}
});
// Subscribe to session expiry
this.timerService.sessionExpired$.subscribe(() => {
console.log('Session expired!');
});
// Subscribe to warning started
this.timerService.warningStarted$.subscribe((seconds) => {
console.log(`Warning started with ${seconds} seconds`);
});
// Subscribe to timer state changes
this.timerService.timerState$.subscribe((state) => {
console.log('Timer state:', state);
});
}
// Get remaining time in seconds
get remainingSeconds() {
return this.timerService.getRemainingTimeSeconds();
}
// Get remaining time in milliseconds
get remainingMs() {
return this.timerService.getRemainingTimeMs();
}
// Check if in warning window
get isInWarningWindow() {
return this.timerService.isInWarningWindow();
}
// Check if session expired
get isExpired() {
return this.timerService.isExpired();
}
// Manually reset timer
resetTimer() {
this.timerService.resetTimer();
}
// Pause timer (e.g., when showing modal)
pauseTimer() {
this.timerService.pause();
}
// Resume timer
resumeTimer() {
this.timerService.resume();
}
}FgsBroadcastService
Cross-tab communication service using BroadcastChannel API.
import { Component, inject, OnInit } from '@angular/core';
import { FgsBroadcastService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent implements OnInit {
private broadcastService = inject(FgsBroadcastService);
ngOnInit() {
// Initialize the service (automatically done by FgsSessionService)
this.broadcastService.initialize();
// Listen for logout from other tabs
this.broadcastService.logoutReceived$.subscribe(() => {
console.log('Logout received from another tab');
});
// Listen for session expired from other tabs
this.broadcastService.sessionExpiredReceived$.subscribe(() => {
console.log('Session expired in another tab');
});
// Listen for session extended from other tabs
this.broadcastService.sessionExtendedReceived$.subscribe(() => {
console.log('Session extended in another tab');
});
// Listen for session locked from other tabs
this.broadcastService.sessionLockedReceived$.subscribe(() => {
console.log('Session locked in another tab');
});
// Listen for session unlocked from other tabs
this.broadcastService.sessionUnlockedReceived$.subscribe(() => {
console.log('Session unlocked in another tab');
});
// Listen for all messages
this.broadcastService.messageReceived$.subscribe((message) => {
console.log('Broadcast message:', message);
});
}
// Get current tab ID
get tabId() {
return this.broadcastService.getTabId();
}
// Broadcast custom message
broadcastMessage() {
this.broadcastService.broadcast('SESSION_EXTENDED', { custom: 'data' });
}
// Distributed locking (prevent race conditions across tabs)
async doSomethingWithLock() {
const lockAcquired = this.broadcastService.acquireLock('my-operation');
if (lockAcquired) {
try {
// Do something that should only run in one tab
await this.performOperation();
} finally {
this.broadcastService.releaseLock('my-operation');
}
} else {
console.log('Another tab is performing this operation');
}
}
// Or use the helper method
async doSomethingWithLockHelper() {
const result = await this.broadcastService.withLock('my-operation', async () => {
return await this.performOperation();
});
if (result === null) {
console.log('Could not acquire lock');
}
}
}FgsNetworkStatusService
Monitor network connectivity.
import { Component, inject, OnInit } from '@angular/core';
import { FgsNetworkStatusService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent implements OnInit {
private networkService = inject(FgsNetworkStatusService);
ngOnInit() {
// Initialize the service (automatically done by FgsSessionService)
this.networkService.initialize();
// Subscribe to going online (includes offline duration)
this.networkService.wentOnline$.subscribe((offlineDurationMs) => {
console.log(`Back online after ${offlineDurationMs}ms`);
});
// Subscribe to going offline
this.networkService.wentOffline$.subscribe(() => {
console.log('Network disconnected');
});
// Subscribe to full status changes
this.networkService.networkStatus$.subscribe((status) => {
console.log('Network status:', status);
});
}
// Check current online status
get isOnline() {
return this.networkService.isOnline;
}
// Get current offline duration (0 if online)
get offlineDuration() {
return this.networkService.currentOfflineDurationMs;
}
// Get full network status
getStatus() {
return this.networkService.getStatus();
}
// Manually check connectivity (makes a test request)
async checkConnectivity() {
const isConnected = await this.networkService.checkConnectivity();
console.log('Connectivity check:', isConnected);
}
}FgsStorageService
Manage session storage (cookies, sessionStorage, localStorage).
import { Component, inject } from '@angular/core';
import { FgsStorageService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent {
private storageService = inject(FgsStorageService);
// Get stored user data
getUserData() {
return this.storageService.getUserData();
}
// Get auth token
getAuthToken() {
return this.storageService.getAuthToken();
}
// Get session ID
getSessionId() {
return this.storageService.getSessionId();
}
// Check if authenticated
isAuthenticated() {
return this.storageService.isAuthenticated();
}
// Clear all session data
clearAll() {
this.storageService.clearAll();
}
}FgsDeviceInfoService
Get device information and fingerprinting.
import { Component, inject } from '@angular/core';
import { FgsDeviceInfoService } from '@sauravart/fgs-session-management';
@Component({...})
export class MyComponent {
private deviceService = inject(FgsDeviceInfoService);
async getDeviceInfo() {
// Get full device info (cached)
const deviceInfo = await this.deviceService.getDeviceInfo();
console.log('Device:', deviceInfo);
// { deviceId, deviceName, browser, browserVersion, os, osVersion, ... }
// Get geolocation (cached)
const geolocation = await this.deviceService.getGeolocation();
console.log('Location:', geolocation);
// { ipAddress, location, country, city, ... }
// Get combined session device info
const sessionInfo = await this.deviceService.getSessionDeviceInfo();
console.log('Session device info:', sessionInfo);
}
// Check device type
get isMobile() {
return this.deviceService.isMobile();
}
get isTablet() {
return this.deviceService.isTablet();
}
get isDesktop() {
return this.deviceService.isDesktop();
}
// Clear cache (force refresh)
clearCache() {
this.deviceService.clearCache();
}
}UI Components
Session Timeout Dialog
Automatically shows when session is about to expire with a beautiful countdown timer.
<fgs-session-timeout-dialog></fgs-session-timeout-dialog>Features:
- Circular SVG countdown timer
- Color-coded countdown (blue > orange > red)
- Extend Session button
- Logout button
- Dark mode support
- Fully accessible (ARIA labels)
Session Conflict Modal
Shows when multiple active sessions are detected.
<fgs-session-conflict-modal></fgs-session-conflict-modal>Features:
- List of active sessions with device info
- Device type icons (desktop/mobile)
- Location and IP address display
- Countdown before force logout is enabled
- Three actions: Wait, Force Logout Others, Cancel & Logout
- Dark mode support
Session Lock Screen
Shows when session is locked for security.
Basic usage (click to unlock):
<fgs-session-lock-screen></fgs-session-lock-screen>With password validation:
<fgs-session-lock-screen
[requirePassword]="true"
[validatePassword]="validatePassword"
(unlockRequested)="onUnlockRequested($event)"
(logoutRequested)="onLogoutRequested()"
></fgs-session-lock-screen>@Component({...})
export class AppComponent {
// Custom password validation function
validatePassword = async (password: string): Promise<boolean> => {
try {
const response = await this.authService.validatePassword(password);
return response.valid;
} catch {
return false;
}
};
onUnlockRequested(event: { password?: string }) {
console.log('Unlock requested');
// If validatePassword returns true, session unlocks automatically
}
onLogoutRequested() {
console.log('Logout requested from lock screen');
}
}Features:
- Beautiful dark gradient background
- Animated lock icon
- User name display (auto-detected)
- Optional password input with visibility toggle
- "Locked duration" display
- Sign out option
- Cross-tab synchronization
- Dark mode by default
Lock/Unlock programmatically:
// Lock the session
this.sessionService.lockSession();
// Unlock the session
this.sessionService.unlockSession();HTTP Interceptor
The interceptor automatically:
- Adds
Authorization: Bearer <token>header - Adds session headers (
X-Session-Id,X-Session-Token,X-Last-Activity) - Records API calls for activity tracking
- Handles 401/403 responses with automatic logout
- Skips authentication for login/signup endpoints
With interceptor (recommended):
provideFgsSessionWithInterceptor(config)Without interceptor (add manually):
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideFgsSession, getFgsSessionInterceptor } from '@sauravart/fgs-session-management';
export const appConfig: ApplicationConfig = {
providers: [
provideFgsSession(config),
provideHttpClient(
withInterceptors([
getFgsSessionInterceptor(),
// ... other interceptors
])
),
],
};Default skip URLs:
/login,/signin,/signup,/register/forgot-password,/reset-password/verify,/oauth/Session/create
Add custom skip URLs:
provideFgsSession({
// ...
interceptorSkipUrls: ['/public', '/health'],
})Route Guards
Available Guards
| Guard | Description |
|-------|-------------|
| fgsSessionGuard | Requires authenticated session |
| fgsSessionGuardChild | For child routes |
| fgsSessionGuardMatch | For lazy-loaded routes (canMatch) |
| fgsGuestGuard | Only for non-authenticated users |
| fgsRoleGuard(roles) | Role-based access control |
| fgsPermissionGuard(permissions, requireAll?) | Permission-based access |
Usage Examples
import { Routes } from '@angular/router';
import {
fgsSessionGuard,
fgsSessionGuardChild,
fgsSessionGuardMatch,
fgsGuestGuard,
fgsRoleGuard,
fgsPermissionGuard,
} from '@sauravart/fgs-session-management';
export const routes: Routes = [
// Guest only (redirect to dashboard if logged in)
{
path: 'login',
component: LoginComponent,
canActivate: [fgsGuestGuard],
},
// Basic authentication guard
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [fgsSessionGuard],
},
// Role-based (user must have ANY of these roles)
{
path: 'admin',
component: AdminComponent,
canActivate: [fgsRoleGuard(['admin', 'manager'])],
},
// Permission-based (user must have ANY of these permissions)
{
path: 'reports',
component: ReportsComponent,
canActivate: [fgsPermissionGuard(['view_reports', 'export_reports'])],
},
// Permission-based (user must have ALL permissions)
{
path: 'settings',
component: SettingsComponent,
canActivate: [fgsPermissionGuard(['read_settings', 'write_settings'], true)],
},
// Lazy-loaded module with canMatch
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes'),
canMatch: [fgsSessionGuardMatch],
},
// Child routes protection
{
path: 'account',
component: AccountLayoutComponent,
canActivate: [fgsSessionGuard],
canActivateChild: [fgsSessionGuardChild],
children: [
{ path: 'profile', component: ProfileComponent },
{ path: 'settings', component: SettingsComponent },
],
},
];Backend API Implementation Guide
Your backend needs to implement the following endpoints to work with this library. All endpoints should accept and return JSON.
Required Endpoints Overview
| Endpoint | Method | Description |
|----------|--------|-------------|
| /session/create | POST | Create a new session after user login |
| /session/validate | POST | Validate if a session is still active |
| /session/refresh | POST | Extend/refresh an existing session |
| /session/revoke | POST | Terminate a specific session |
| /session/list | GET | List all active sessions for a user |
| /auth/logout | POST | Logout and cleanup session |
1. Create Session
Creates a new session when a user logs in. Called automatically after initializeSession().
Endpoint: POST /session/create
Request Headers:
Content-Type: application/json
Authorization: Bearer <auth_token>Request Body:
{
"userId": "user-123",
"userCode": "USR001",
"deviceId": "d4f5e6a7-b8c9-4d3e-a2b1-c0d9e8f7a6b5",
"deviceName": "Chrome on Windows",
"browser": "Chrome",
"browserVersion": "120.0.0",
"os": "Windows",
"osVersion": "10",
"ipAddress": "103.25.142.85",
"location": "Mumbai, IN",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ...",
"screenResolution": "1920x1080",
"timezone": "Asia/Kolkata",
"language": "en-IN"
}Success Response (200 OK):
{
"success": true,
"sessionId": "sess_abc123def456",
"sessionAuthToken": "sat_xyz789...",
"authToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2024-01-15T12:30:00Z",
"message": "Session created successfully"
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 400 | INVALID_REQUEST | Missing required fields |
| 401 | UNAUTHORIZED | Invalid or expired auth token |
| 409 | SESSION_CONFLICT | Another active session exists (if single-session policy) |
| 500 | SERVER_ERROR | Internal server error |
Error Response Format:
{
"success": false,
"error": "SESSION_CONFLICT",
"message": "Another active session already exists",
"activeSessions": [
{
"sessionId": "sess_existing123",
"deviceName": "Firefox on MacOS",
"ipAddress": "49.36.178.92",
"location": "Bangalore, IN",
"lastActivity": "2024-01-15T15:55:00Z"
}
]
}Implementation Notes:
- Generate a unique
sessionId(UUID recommended) - Store session with: userId, deviceId, deviceName, ipAddress, createdAt, expiresAt, lastActivity
- Optionally return a new
authTokenwith sessionId embedded in JWT claims - Consider implementing max sessions per user policy
2. Validate Session
Validates if the current session is still active. Called periodically (every 60 seconds by default) and when the tab regains focus.
Endpoint: POST /session/validate
Request Headers:
Content-Type: application/json
Authorization: Bearer <auth_token>
X-Session-Id: sess_abc123def456
X-Session-Token: sat_xyz789...Request Body:
{
"sessionId": "sess_abc123def456"
}Success Response (200 OK):
{
"valid": true,
"sessionId": "sess_abc123def456",
"expiresAt": "2024-01-15T12:30:00Z",
"message": "Session is valid"
}Invalid Session Response (200 OK):
{
"valid": false,
"message": "Session has expired or was revoked"
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 401 | UNAUTHORIZED | Invalid auth token |
| 404 | SESSION_NOT_FOUND | Session doesn't exist |
Implementation Notes:
- Check if session exists and hasn't expired
- Check if session wasn't revoked/terminated
- Update
lastActivitytimestamp on successful validation - Return
valid: falseinstead of 401 for expired sessions (allows graceful handling)
3. Refresh Session
Extends the session expiry time. Called when user clicks "Extend Session" or on user activity (throttled).
Endpoint: POST /session/refresh
Request Headers:
Content-Type: application/json
Authorization: Bearer <auth_token>
X-Session-Id: sess_abc123def456
X-Session-Token: sat_xyz789...
X-Last-Activity: 2024-01-15T10:20:00ZRequest Body:
{
"sessionId": "sess_abc123def456"
}Success Response (200 OK):
{
"success": true,
"sessionId": "sess_abc123def456",
"expiresAt": "2024-01-15T12:45:00Z",
"message": "Session extended successfully"
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 401 | UNAUTHORIZED | Invalid auth token |
| 404 | SESSION_NOT_FOUND | Session doesn't exist |
| 410 | SESSION_EXPIRED | Session has already expired |
Implementation Notes:
- Extend
expiresAtby the configured session timeout duration - Update
lastActivitytimestamp - Reject if session is already expired or revoked
4. Revoke Session
Terminates a specific session. Used when user wants to logout other sessions or force logout from conflict modal.
Endpoint: POST /session/revoke
Request Headers:
Content-Type: application/json
Authorization: Bearer <auth_token>
X-Session-Id: sess_abc123def456Request Body:
{
"sessionId": "sess_other789",
"reason": "user_requested"
}Revoke All Other Sessions:
{
"revokeAll": true,
"exceptSessionId": "sess_abc123def456"
}Success Response (200 OK):
{
"success": true,
"message": "Session revoked successfully",
"revokedCount": 1
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 401 | UNAUTHORIZED | Invalid auth token |
| 403 | FORBIDDEN | Cannot revoke another user's session |
| 404 | SESSION_NOT_FOUND | Session doesn't exist |
Implementation Notes:
- Mark session as revoked/terminated in database
- Only allow users to revoke their own sessions
- Support revoking all sessions except current one
- Consider sending push notification to revoked session
5. List Sessions
Returns all active sessions for the current user. Used for multi-session conflict detection.
Endpoint: GET /session/list
Request Headers:
Authorization: Bearer <auth_token>
X-Session-Id: sess_abc123def456Query Parameters (optional):
?userId=user-123&includeExpired=falseSuccess Response (200 OK):
{
"success": true,
"sessions": [
{
"sessionId": "sess_abc123def456",
"deviceName": "Chrome on Windows",
"browser": "Chrome",
"browserVersion": "120.0.0",
"os": "Windows",
"osVersion": "10",
"ipAddress": "103.25.142.85",
"location": "Mumbai, IN",
"lastActivity": "2024-01-15T15:55:00Z",
"createdAt": "2024-01-15T13:30:00Z",
"expiresAt": "2024-01-15T18:00:00Z",
"isCurrent": true
},
{
"sessionId": "sess_xyz789012",
"deviceName": "Safari on iPhone",
"browser": "Safari",
"browserVersion": "17.0",
"os": "iOS",
"osVersion": "17.2",
"ipAddress": "49.36.178.92",
"location": "Bangalore, IN",
"lastActivity": "2024-01-15T14:45:00Z",
"createdAt": "2024-01-15T13:00:00Z",
"expiresAt": "2024-01-15T17:00:00Z",
"isCurrent": false
}
],
"totalCount": 2
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 401 | UNAUTHORIZED | Invalid auth token |
Implementation Notes:
- Return only active (non-expired, non-revoked) sessions by default
- Mark the current session with
isCurrent: true - Sort by
lastActivitydescending (most recent first) - Include device/location info for user to identify sessions
6. Logout
Logs out the user and terminates the current session.
Endpoint: POST /auth/logout
Request Headers:
Content-Type: application/json
Authorization: Bearer <auth_token>
X-Session-Id: sess_abc123def456Request Body (optional):
{
"sessionId": "sess_abc123def456",
"logoutAllSessions": false
}Success Response (200 OK):
{
"success": true,
"message": "Logged out successfully"
}Error Responses:
| Status | Code | Description |
|--------|------|-------------|
| 401 | UNAUTHORIZED | Invalid auth token (still perform cleanup) |
Implementation Notes:
- Revoke the current session
- Optionally revoke all sessions if
logoutAllSessions: true - Invalidate the auth token if using token blacklist
- Always return 200 even if session already expired (idempotent)
Common HTTP Headers
Request Headers Sent by Library
| Header | Description | Example |
|--------|-------------|---------|
| Authorization | Bearer token for authentication | Bearer eyJhbGciOi... |
| X-Session-Id | Current session identifier | sess_abc123def456 |
| X-Session-Token | Session-specific auth token | sat_xyz789... |
| X-Last-Activity | Timestamp of last user activity | 2024-01-15T10:20:00Z |
| Content-Type | Request body format | application/json |
Response Headers (Recommended)
| Header | Description | Example |
|--------|-------------|---------|
| X-Session-Expires | When the session will expire | 2024-01-15T12:30:00Z |
| X-Session-Remaining | Seconds until expiry | 3600 |
Error Response Format
All error responses should follow this format for consistent handling:
{
"success": false,
"error": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"field": "Additional context if needed"
}
}Standard Error Codes
| Code | HTTP Status | Description |
|------|-------------|-------------|
| UNAUTHORIZED | 401 | Invalid or missing auth token |
| FORBIDDEN | 403 | Valid token but insufficient permissions |
| SESSION_NOT_FOUND | 404 | Session ID doesn't exist |
| SESSION_EXPIRED | 410 | Session has expired |
| SESSION_CONFLICT | 409 | Another active session exists |
| INVALID_REQUEST | 400 | Missing or invalid request parameters |
| RATE_LIMITED | 429 | Too many requests |
| SERVER_ERROR | 500 | Internal server error |
Database Schema (Recommended)
CREATE TABLE sessions (
id VARCHAR(50) PRIMARY KEY, -- sessionId (e.g., sess_abc123)
user_id VARCHAR(50) NOT NULL, -- Reference to users table
user_code VARCHAR(50), -- Optional user code
device_id VARCHAR(100) NOT NULL, -- Unique device fingerprint
device_name VARCHAR(255), -- Human-readable device name
browser VARCHAR(50),
browser_version VARCHAR(20),
os VARCHAR(50),
os_version VARCHAR(20),
ip_address VARCHAR(45), -- Supports IPv6
location VARCHAR(255),
user_agent TEXT,
screen_resolution VARCHAR(20),
timezone VARCHAR(50),
language VARCHAR(10),
session_auth_token VARCHAR(255), -- Optional session-specific token
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NOT NULL,
last_activity TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
revoked_at TIMESTAMP, -- NULL if active
revoke_reason VARCHAR(50), -- 'user_requested', 'expired', 'forced', etc.
is_active BOOLEAN GENERATED ALWAYS AS (revoked_at IS NULL AND expires_at > CURRENT_TIMESTAMP),
INDEX idx_user_active (user_id, is_active),
INDEX idx_expires (expires_at),
INDEX idx_device (device_id)
);API Response Formats
Create Session Request
interface CreateSessionRequest {
userId: string;
userCode?: string;
deviceId: string;
deviceName: string;
browser: string;
browserVersion?: string;
os: string;
osVersion?: string;
ipAddress: string;
location?: string;
userAgent: string;
screenResolution?: string;
timezone?: string;
language?: string;
}Create Session Response
interface CreateSessionResponse {
sessionId: string;
sessionAuthToken?: string;
authToken?: string; // New token with sessionId embedded
expiresAt?: string;
message?: string;
success?: boolean;
}Validate Session Response
interface ValidateSessionResponse {
valid: boolean;
sessionId?: string;
expiresAt?: string;
message?: string;
}Active Sessions Response
interface ActiveSessionResponse {
sessionId: string;
deviceName?: string;
browser?: string;
os?: string;
ipAddress?: string;
location?: string;
lastActivity?: string;
createdAt?: string;
isCurrent?: boolean;
}Tailwind CSS Setup
The UI components use Tailwind CSS classes. If you're using Tailwind, add the library to your content configuration:
// tailwind.config.js
module.exports = {
content: [
"./src/**/*.{html,ts}",
"./node_modules/@sauravart/fgs-session-management/**/*.{html,ts,mjs}"
],
darkMode: 'class', // or 'media'
// ... rest of config
}Without Tailwind: The components include all necessary styles and will work without Tailwind installed, though custom Tailwind themes won't apply.
Browser Support
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
BroadcastChannel API Fallback: For browsers that don't support BroadcastChannel, the library automatically falls back to localStorage events for cross-tab communication.
Changelog
v1.1.0
- Added cross-tab synchronization using BroadcastChannel API
- Added
FgsBroadcastServicefor cross-tab communication - Added
FgsNetworkStatusServicefor online/offline detection - Added session lock/unlock feature with
FgsSessionLockScreenComponent - Added proactive session refresh before expiry
- Added error recovery with retry for session creation (3 attempts with exponential backoff)
- Added distributed locking for conflict detection to prevent race conditions
- Added periodic session validation (every 60 seconds)
- Added network status handling (validates session when coming back online)
- Fixed race condition in multi-tab conflict detection
- Changed
useLegacyAuthHeaderdefault tofalse - Added new UI configuration options:
showLockScreen,lockScreenTitle,lockScreenMessage - Added
@angular/formsas peer dependency
v1.0.0
- Initial release
- Session timeout management with configurable timeout
- Activity tracking (mouse, keyboard, scroll, touch, visibility)
- Multi-session conflict detection
- UI components (timeout dialog, conflict modal)
- Route guards (session, guest, role, permission)
- HTTP interceptor with automatic token handling
- Device fingerprinting
- IP geolocation support
License
MIT License - see LICENSE for details.
Author
Figment Global Solutions
- Website: figmentglobal.com
- GitHub: FigmentGlobalSolution
Support
For issues and feature requests, please visit: https://github.com/FigmentGlobalSolution/session_management/issues
