@bernierllc/session-manager
v1.0.6
Published
Secure session management with multiple storage backends and authentication integration
Readme
@bernierllc/session-manager
Secure session management with multiple storage backends and authentication integration.
Installation
npm install @bernierllc/session-manager @bernierllc/crypto-utilsFor Redis support:
npm install @bernierllc/session-manager redisFor Express middleware:
npm install @bernierllc/session-manager cookieQuick Start
import { SessionManager, MemorySessionStorage } from '@bernierllc/session-manager';
// Create session manager
const sessionManager = new SessionManager({
storage: new MemorySessionStorage(),
secret: process.env.SESSION_SECRET!,
maxAge: 24 * 60 * 60 * 1000 // 24 hours
});
// Create new session
const session = await sessionManager.create({
userId: '123',
roles: ['user'],
loginTime: new Date()
});
// Retrieve session
const retrievedSession = await sessionManager.get(session.id);
console.log('Session data:', retrievedSession?.data);Core Features
- Multiple Storage: Memory, Redis, database, encrypted cookies
- Security: Secure session IDs, CSRF protection, session fixation prevention
- Lifecycle Management: TTL, sliding expiration, automatic cleanup
- Authentication Integration: User binding, role-based sessions
- Framework Support: Express, Fastify, Next.js middleware
- Serialization: JSON, encrypted, compressed session data
- Monitoring: Session analytics and security tracking
API Reference
SessionManager
Constructor
const sessionManager = new SessionManager(options: SessionOptions)Options:
storage: SessionStorage- Storage backend (required)secret: string- Signing/encryption secret (required)name?: string- Session cookie name (default: 'sessionId')maxAge?: number- Session TTL in milliseconds (default: 24 hours)secure?: boolean- HTTPS only cookies (default: false)httpOnly?: boolean- HTTP only cookies (default: true)sameSite?: 'strict' | 'lax' | 'none'- SameSite cookie attribute (default: 'lax')rolling?: boolean- Refresh TTL on access (default: true)genid?: () => string- Custom session ID generator
Core Methods
// Session operations
await sessionManager.create(initialData?: SessionData): Promise<Session>
await sessionManager.get(sessionId: string): Promise<Session | null>
await sessionManager.destroy(sessionId: string): Promise<void>
await sessionManager.destroyAll(): Promise<void>
// User session management
await sessionManager.getSessionsForUser(userId: string): Promise<Session[]>
await sessionManager.destroyUserSessions(userId: string): Promise<void>
await sessionManager.destroyAllButCurrent(currentSessionId: string, userId: string): Promise<void>
// Security
await sessionManager.regenerateId(session: Session): Promise<void>
await sessionManager.validateSession(sessionId: string, ipAddress?: string, userAgent?: string): Promise<boolean>
// Cleanup
await sessionManager.cleanup(): Promise<number>
sessionManager.startCleanupInterval(intervalMs?: number): void
sessionManager.stopCleanupInterval(): void
// Statistics
await sessionManager.getStats(): Promise<SessionStats>Session
// Data operations
session.get<T>(key: string): T | undefined
session.set<T>(key: string, value: T): void
session.has(key: string): boolean
session.delete(key: string): boolean
session.clear(): void
session.touch(): void
// Persistence
await session.save(): Promise<void>
await session.reload(): Promise<void>
await session.destroy(): Promise<void>Usage Examples
Basic Session Management
import { SessionManager, MemorySessionStorage } from '@bernierllc/session-manager';
const sessionManager = new SessionManager({
storage: new MemorySessionStorage({ maxSessions: 1000 }),
secret: process.env.SESSION_SECRET!,
maxAge: 60 * 60 * 1000, // 1 hour
rolling: true
});
// Create session with user data
const session = await sessionManager.create({
userId: '123',
username: 'john_doe',
roles: ['user', 'premium']
});
// Use session
session.set('lastActivity', new Date());
session.set('preferences', { theme: 'dark', lang: 'en' });
await session.save();Redis-backed Distributed Sessions
import { SessionManager, RedisSessionStorage } from '@bernierllc/session-manager';
const sessionManager = new SessionManager({
storage: new RedisSessionStorage({
host: 'localhost',
port: 6379,
keyPrefix: 'sess:',
password: process.env.REDIS_PASSWORD
}),
secret: process.env.SESSION_SECRET!,
maxAge: 30 * 60 * 1000, // 30 minutes
rolling: true
});
// Sessions work across multiple application instances
const session = await sessionManager.create({ userId: '456' });Express Middleware Integration
import express from 'express';
import { createSessionMiddleware, RedisSessionStorage } from '@bernierllc/session-manager';
const app = express();
const sessionMiddleware = createSessionMiddleware({
storage: new RedisSessionStorage({
host: 'localhost',
port: 6379
}),
secret: process.env.SESSION_SECRET!,
name: 'sid',
maxAge: 24 * 60 * 60 * 1000, // 24 hours
secure: process.env.NODE_ENV === 'production',
rolling: true
});
app.use(sessionMiddleware);
// Login route
app.post('/login', async (req, res) => {
const { username, password } = req.body;
if (await validateCredentials(username, password)) {
const user = await getUserByUsername(username);
req.session.set('userId', user.id);
req.session.set('roles', user.roles);
req.session.set('loginTime', new Date());
await req.session.save();
res.json({ success: true });
} else {
res.status(401).json({ error: 'Invalid credentials' });
}
});
// Logout route
app.post('/logout', async (req, res) => {
await req.session.destroy();
res.clearCookie('sid');
res.json({ success: true });
});
// Protected route
app.get('/profile', async (req, res) => {
const userId = req.session.get('userId');
if (!userId) {
return res.status(401).json({ error: 'Not authenticated' });
}
const user = await getUserById(userId);
res.json(user);
});Advanced Security Features
import { SessionManager } from '@bernierllc/session-manager';
const sessionManager = new SessionManager({
// ... configuration
});
// Session validation with security checks
async function secureSessionValidation(sessionId: string, req: any) {
const isValid = await sessionManager.validateSession(
sessionId,
req.ip,
req.headers['user-agent']
);
if (!isValid) {
throw new Error('Session validation failed');
}
const session = await sessionManager.get(sessionId);
// Additional security checks
if (session && session.data.ipAddress !== req.ip) {
console.warn(`Suspicious activity: IP changed for session ${sessionId}`);
await sessionManager.destroy(sessionId);
throw new Error('Session terminated due to security concerns');
}
return session;
}
// Session regeneration for privilege escalation
async function escalatePrivileges(session: Session, newRoles: string[]) {
// Regenerate session ID for security
await sessionManager.regenerateId(session);
// Update roles
session.set('roles', newRoles);
session.set('privilegeEscalation', new Date());
await session.save();
}Multi-Device Session Management
// Limit concurrent sessions per user
async function enforceSessionLimit(userId: string, maxSessions: number = 3) {
const userSessions = await sessionManager.getSessionsForUser(userId);
if (userSessions.length >= maxSessions) {
// Sort by last accessed time, keep most recent
userSessions.sort((a, b) =>
b.lastAccessedAt.getTime() - a.lastAccessedAt.getTime()
);
// Destroy oldest sessions
const sessionsToDestroy = userSessions.slice(maxSessions - 1);
for (const session of sessionsToDestroy) {
await session.destroy();
}
}
}
// Logout from all devices except current
app.post('/logout-all-devices', async (req, res) => {
const currentSessionId = req.sessionID;
const userId = req.session.get('userId');
await sessionManager.destroyAllButCurrent(currentSessionId, userId);
res.json({
success: true,
message: 'Logged out from all other devices'
});
});Session Analytics and Monitoring
// Monitor session activity
setInterval(async () => {
const stats = await sessionManager.getStats();
console.log(`Session Statistics:
Active Sessions: ${stats.active}
Total Created: ${stats.totalCreated}
Peak Concurrent: ${stats.peakConcurrent}
Average Duration: ${Math.round(stats.averageDuration / 1000)}s
`);
// Alert on suspicious activity
if (stats.active > stats.peakConcurrent * 1.2) {
console.warn('Unusually high session activity detected');
}
}, 60000); // Check every minute
// Automatic cleanup
sessionManager.startCleanupInterval(15 * 60 * 1000); // Clean every 15 minutes
// Graceful shutdown
process.on('SIGTERM', async () => {
console.log('Shutting down session manager...');
await sessionManager.close();
process.exit(0);
});Configuration
Storage Backends
MemorySessionStorage
new MemorySessionStorage({
maxSessions: 1000 // Maximum number of sessions in memory
})RedisSessionStorage
new RedisSessionStorage({
host: 'localhost',
port: 6379,
password: 'secret', // Optional
db: 0, // Database number
keyPrefix: 'sess:', // Key prefix for sessions
connectionString: 'redis://localhost:6379' // Alternative to host/port
})Security Best Practices
- Use Strong Secrets: Generate cryptographically secure secrets
- Enable Secure Cookies: Set
secure: truein production - Regular Cleanup: Enable automatic session cleanup
- Session Regeneration: Regenerate session IDs after authentication
- IP Validation: Consider IP address validation for high-security apps
- Rolling Sessions: Use rolling expiration to maintain active sessions
Performance Optimization
// Optimize for high-traffic applications
const sessionManager = new SessionManager({
storage: new RedisSessionStorage({
// Redis connection pooling
host: 'redis-cluster',
port: 6379
}),
// Shorter cleanup intervals for active cleanup
maxAge: 30 * 60 * 1000, // 30 minutes
rolling: true, // Keep active sessions alive
saveUninitialized: false, // Don't save empty sessions
resave: false // Don't resave unchanged sessions
});
// Efficient cleanup
sessionManager.startCleanupInterval(5 * 60 * 1000); // 5 minutesError Handling
try {
const session = await sessionManager.get(sessionId);
if (!session) {
throw new Error('Session not found or expired');
}
// Use session...
} catch (error) {
console.error('Session error:', error);
// Handle session errors appropriately
}Testing
The package includes comprehensive tests covering:
- Session lifecycle management
- Security features and validation
- Storage backend implementations
- Memory management and cleanup
- Express middleware integration
Run tests:
npm test
npm run test:coverageIntegration Documentation
Logger Integration
The session manager supports optional logger integration using @bernierllc/logger:
import { SessionManager } from '@bernierllc/session-manager';
import { detectLogger } from '@bernierllc/logger';
const sessionManager = new SessionManager({
storage: new MemorySessionStorage(),
secret: process.env.SESSION_SECRET!
});
// Auto-detect logger if available
const logger = await detectLogger();
if (logger) {
// Enhanced logging for session events
sessionManager.on('sessionCreated', (sessionId) => {
logger.info('Session created', { sessionId });
});
sessionManager.on('sessionDestroyed', (sessionId) => {
logger.info('Session destroyed', { sessionId });
});
}NeverHub Integration
The session manager integrates with NeverHub when available for enhanced service discovery and event communication:
import { SessionManager } from '@bernierllc/session-manager';
import { detectNeverHub } from '@bernierllc/neverhub-adapter';
async function initializeSessionManager() {
const sessionManager = new SessionManager({
storage: new MemorySessionStorage(),
secret: process.env.SESSION_SECRET!
});
// Auto-detect NeverHub
const neverhub = await detectNeverHub();
if (neverhub) {
// Register session manager as a service
await neverhub.register({
type: 'session-manager',
name: '@bernierllc/session-manager',
version: '1.0.0',
capabilities: [
{ type: 'session', name: 'management', version: '1.0.0' },
{ type: 'auth', name: 'session-auth', version: '1.0.0' }
]
});
// Publish session events
sessionManager.on('sessionCreated', async (data) => {
await neverhub.publishEvent({
type: 'session.created',
data: { sessionId: data.sessionId, userId: data.userId }
});
});
// Subscribe to authentication events
await neverhub.subscribe('auth.login', async (event) => {
// Create session for authenticated user
await sessionManager.create({
userId: event.data.userId,
roles: event.data.roles
});
});
}
return sessionManager;
}Graceful Degradation
The session manager implements graceful degradation patterns:
- Works without external services: Core functionality operates independently
- Logger integration: Enhanced logging when logger service is available
- NeverHub integration: Service discovery and events when NeverHub is present
- Storage flexibility: Falls back to memory storage if Redis is unavailable
Dependencies
- Required:
@bernierllc/crypto-utilsfor secure session ID generation - Optional:
redisfor Redis storage backend - Optional:
cookiefor Express middleware support - Optional:
@bernierllc/loggerfor enhanced logging capabilities - Optional:
@bernierllc/neverhub-adapterfor service discovery integration
License
Copyright (c) 2025 Bernier LLC. All rights reserved.
