Files
web-cad/backend/src/auth/AuthService.ts
T

99 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AuthService Registration, Login, Session Management
*/
import bcrypt from 'bcrypt';
import { randomUUID } from 'crypto';
import type { DatabaseInterface, DBUser, DBSession } from '../database/DatabaseInterface.js';
const SALT_ROUNDS = 10;
const SESSION_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
export interface Session {
token: string;
userId: string;
expiresAt: number;
}
export interface AuthResult {
user: Omit<DBUser, 'password_hash'>;
session: Session;
}
export class AuthService {
constructor(private db: DatabaseInterface) {}
async register(email: string, password: string, name: string, role: string = 'planer'): Promise<AuthResult> {
const existing = this.db.getUserByEmail(email);
if (existing) {
throw new Error('Email already registered');
}
const password_hash = await bcrypt.hash(password, SALT_ROUNDS);
const user = this.db.createUser({ email, password_hash, name, role });
const session = this.createSession(user.id);
return { user: this.sanitizeUser(user), session };
}
async login(email: string, password: string): Promise<AuthResult> {
const user = this.db.getUserByEmail(email);
if (!user) {
throw new Error('Invalid credentials');
}
const valid = await bcrypt.compare(password, user.password_hash);
if (!valid) {
throw new Error('Invalid credentials');
}
const session = this.createSession(user.id);
return { user: this.sanitizeUser(user), session };
}
async changePassword(userId: string, oldPassword: string, newPassword: string): Promise<void> {
const user = this.db.getUser(userId);
if (!user) throw new Error('User not found');
const valid = await bcrypt.compare(oldPassword, user.password_hash);
if (!valid) throw new Error('Invalid current password');
const password_hash = await bcrypt.hash(newPassword, SALT_ROUNDS);
this.db.updateUser(userId, { password_hash });
}
createSession(userId: string): Session {
const token = randomUUID();
const expiresAt = Date.now() + SESSION_TTL_MS;
this.db.createSession({ token, user_id: userId, expires_at: expiresAt });
return { token, userId, expiresAt };
}
validateSession(token: string): Session | null {
const dbSession = this.db.getSession(token);
if (!dbSession) return null;
if (Date.now() > dbSession.expires_at) {
this.db.deleteSession(token);
return null;
}
return {
token: dbSession.token,
userId: dbSession.user_id,
expiresAt: dbSession.expires_at,
};
}
destroySession(token: string): void {
this.db.deleteSession(token);
}
getUserFromSession(token: string): DBUser | null {
const session = this.validateSession(token);
if (!session) return null;
return this.db.getUser(session.userId);
}
private sanitizeUser(user: DBUser): Omit<DBUser, 'password_hash'> {
const { password_hash, ...safe } = user;
return safe;
}
}