# 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.

[![npm version](https://badge.fury.io/js/%40sauravart%2Ffgs-session-management.svg)](https://www.npmjs.com/package/@sauravart/fgs-session-management)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 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

```bash
npm install @sauravart/fgs-session-management
```

## Requirements

- Angular 17, 18, or 19
- RxJS 7.x

## Quick Start

### 1. Configure the Provider

In your `app.config.ts`:

```typescript
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`:

```typescript
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:

```typescript
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

```typescript
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

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
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.

```typescript
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).

```typescript
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.

```typescript
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.

```html
<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.

```html
<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):**
```html
<fgs-session-lock-screen></fgs-session-lock-screen>
```

**With password validation:**
```html
<fgs-session-lock-screen
  [requirePassword]="true"
  [validatePassword]="validatePassword"
  (unlockRequested)="onUnlockRequested($event)"
  (logoutRequested)="onLogoutRequested()"
></fgs-session-lock-screen>
```

```typescript
@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:**
```typescript
// 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):**
```typescript
provideFgsSessionWithInterceptor(config)
```

**Without interceptor (add manually):**
```typescript
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:
```typescript
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

```typescript
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:**
```json
{
  "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):**
```json
{
  "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:**
```json
{
  "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 `authToken` with 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:**
```json
{
  "sessionId": "sess_abc123def456"
}
```

**Success Response (200 OK):**
```json
{
  "valid": true,
  "sessionId": "sess_abc123def456",
  "expiresAt": "2024-01-15T12:30:00Z",
  "message": "Session is valid"
}
```

**Invalid Session Response (200 OK):**
```json
{
  "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 `lastActivity` timestamp on successful validation
- Return `valid: false` instead 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:00Z
```

**Request Body:**
```json
{
  "sessionId": "sess_abc123def456"
}
```

**Success Response (200 OK):**
```json
{
  "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 `expiresAt` by the configured session timeout duration
- Update `lastActivity` timestamp
- 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_abc123def456
```

**Request Body:**
```json
{
  "sessionId": "sess_other789",
  "reason": "user_requested"
}
```

**Revoke All Other Sessions:**
```json
{
  "revokeAll": true,
  "exceptSessionId": "sess_abc123def456"
}
```

**Success Response (200 OK):**
```json
{
  "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_abc123def456
```

**Query Parameters (optional):**
```
?userId=user-123&includeExpired=false
```

**Success Response (200 OK):**
```json
{
  "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 `lastActivity` descending (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_abc123def456
```

**Request Body (optional):**
```json
{
  "sessionId": "sess_abc123def456",
  "logoutAllSessions": false
}
```

**Success Response (200 OK):**
```json
{
  "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:

```json
{
  "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)

```sql
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

```typescript
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

```typescript
interface CreateSessionResponse {
  sessionId: string;
  sessionAuthToken?: string;
  authToken?: string;      // New token with sessionId embedded
  expiresAt?: string;
  message?: string;
  success?: boolean;
}
```

### Validate Session Response

```typescript
interface ValidateSessionResponse {
  valid: boolean;
  sessionId?: string;
  expiresAt?: string;
  message?: string;
}
```

### Active Sessions Response

```typescript
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:

```javascript
// 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 `FgsBroadcastService` for cross-tab communication
- Added `FgsNetworkStatusService` for 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 `useLegacyAuthHeader` default to `false`
- Added new UI configuration options: `showLockScreen`, `lockScreenTitle`, `lockScreenMessage`
- Added `@angular/forms` as 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](LICENSE) for details.

## Author

**Figment Global Solutions**

- Website: [figmentglobal.com](https://figmentglobal.com)
- GitHub: [FigmentGlobalSolution](https://github.com/FigmentGlobalSolution)

## Support

For issues and feature requests, please visit:
https://github.com/FigmentGlobalSolution/session_management/issues
