@ordojs/security
Version:
Security package for OrdoJS with XSS, CSRF, and injection protection
175 lines • 5.16 kB
JavaScript
/**
* CSRF Session Manager
* Manages CSRF sessions and token storage
*/
export class CSRFSessionManager {
sessions = new Map();
config;
cleanupInterval = null;
constructor(config) {
this.config = config;
this.startCleanupTimer();
}
/**
* Create a new CSRF session
*/
createSession(sessionId) {
const session = {
id: sessionId,
tokens: new Map(),
createdAt: Date.now(),
lastActivity: Date.now()
};
this.sessions.set(sessionId, session);
return session;
}
/**
* Get an existing session or create a new one
*/
getOrCreateSession(sessionId) {
let session = this.sessions.get(sessionId);
if (!session) {
session = this.createSession(sessionId);
}
else {
// Update last activity
session.lastActivity = Date.now();
}
return session;
}
/**
* Add a token to a session
*/
addToken(sessionId, token) {
const session = this.getOrCreateSession(sessionId);
session.tokens.set(token.value, token);
// Clean up expired tokens for this session
this.cleanupSessionTokens(session);
}
/**
* Validate a token for a session
*/
validateSessionToken(sessionId, tokenValue) {
const session = this.sessions.get(sessionId);
if (!session) {
return { valid: false, error: 'Session not found' };
}
const token = session.tokens.get(tokenValue);
if (!token) {
return { valid: false, error: 'Token not found in session' };
}
// Check if token has expired
if (Date.now() > token.expiresAt) {
// Remove expired token
session.tokens.delete(tokenValue);
return { valid: false, error: 'Token expired', expired: true };
}
// Update session activity
session.lastActivity = Date.now();
return { valid: true };
}
/**
* Remove a token from a session (for one-time use tokens)
*/
consumeToken(sessionId, tokenValue) {
const session = this.sessions.get(sessionId);
if (!session) {
return false;
}
return session.tokens.delete(tokenValue);
}
/**
* Get all active tokens for a session
*/
getSessionTokens(sessionId) {
const session = this.sessions.get(sessionId);
if (!session) {
return [];
}
// Clean up expired tokens first
this.cleanupSessionTokens(session);
return Array.from(session.tokens.values());
}
/**
* Remove a session and all its tokens
*/
removeSession(sessionId) {
return this.sessions.delete(sessionId);
}
/**
* Clean up expired tokens for a specific session
*/
cleanupSessionTokens(session) {
const now = Date.now();
const expiredTokens = [];
for (const [tokenValue, token] of session.tokens) {
if (now > token.expiresAt) {
expiredTokens.push(tokenValue);
}
}
expiredTokens.forEach(tokenValue => {
session.tokens.delete(tokenValue);
});
}
/**
* Clean up expired sessions and tokens
*/
cleanup() {
const now = Date.now();
const sessionTimeout = this.config.tokenExpiry * 2; // Sessions expire after 2x token expiry
const expiredSessions = [];
for (const [sessionId, session] of this.sessions) {
// Remove sessions that haven't been active for too long
if (now - session.lastActivity > sessionTimeout) {
expiredSessions.push(sessionId);
continue;
}
// Clean up expired tokens in active sessions
this.cleanupSessionTokens(session);
}
// Remove expired sessions
expiredSessions.forEach(sessionId => {
this.sessions.delete(sessionId);
});
}
/**
* Start automatic cleanup timer
*/
startCleanupTimer() {
// Run cleanup every 15 minutes
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, 15 * 60 * 1000);
}
/**
* Stop cleanup timer
*/
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
this.sessions.clear();
}
/**
* Get session statistics
*/
getStats() {
let totalTokens = 0;
let activeSessions = 0;
const now = Date.now();
const sessionTimeout = this.config.tokenExpiry * 2;
for (const session of this.sessions.values()) {
totalTokens += session.tokens.size;
if (now - session.lastActivity <= sessionTimeout) {
activeSessions++;
}
}
return {
totalSessions: this.sessions.size,
totalTokens,
activeSessions
};
}
}
//# sourceMappingURL=session-manager.js.map