import crypto from 'crypto';
export class AuthManager {
private validTokens: Set<string>;
private tokenExpiry: Map<string, number>;
constructor() {
this.validTokens = new Set();
this.tokenExpiry = new Map();
}
/**
* Validate an authentication token
*/
validateToken(token: string | undefined, expectedToken?: string): boolean {
if (!expectedToken) {
// No authentication required
return true;
}
if (!token) {
return false;
}
// Check static token
if (token === expectedToken) {
return true;
}
// Check dynamic tokens
if (this.validTokens.has(token)) {
const expiry = this.tokenExpiry.get(token);
if (expiry && expiry > Date.now()) {
return true;
} else {
// Token expired
this.validTokens.delete(token);
this.tokenExpiry.delete(token);
return false;
}
}
return false;
}
/**
* Generate a new authentication token
*/
generateToken(expiryHours: number = 24): string {
const token = crypto.randomBytes(32).toString('hex');
const expiryTime = Date.now() + (expiryHours * 60 * 60 * 1000);
this.validTokens.add(token);
this.tokenExpiry.set(token, expiryTime);
// Clean up expired tokens
this.cleanupExpiredTokens();
return token;
}
/**
* Revoke a token
*/
revokeToken(token: string): void {
this.validTokens.delete(token);
this.tokenExpiry.delete(token);
}
/**
* Clean up expired tokens
*/
private cleanupExpiredTokens(): void {
const now = Date.now();
for (const [token, expiry] of this.tokenExpiry.entries()) {
if (expiry <= now) {
this.validTokens.delete(token);
this.tokenExpiry.delete(token);
}
}
}
/**
* Hash a password or token for secure storage
*/
static hashToken(token: string): string {
return crypto.createHash('sha256').update(token).digest('hex');
}
/**
* Compare a plain token with a hashed token
*/
static compareTokens(plainToken: string, hashedToken: string): boolean {
const hashedPlainToken = AuthManager.hashToken(plainToken);
return crypto.timingSafeEqual(
Buffer.from(hashedPlainToken),
Buffer.from(hashedToken)
);
}
}