UNPKG

@mrhsp/auth-backend

Version:

Gate Keeper Backend Authentication Package

84 lines (83 loc) 3.04 kB
import jwt from 'jsonwebtoken'; const { sign, verify } = jwt; import { compare, hash } from "bcrypt"; import { validatePassword } from "./utils/validate-password.js"; import { InMemoryAdapter } from "./in-memory-adapter.js"; import { InvalidCredentialsError, AccountAlreadyExistsError, AccountNotFoundError, } from "./errors.js"; const DEFAULT_JWT_OPTIONS = { secret: "", expiresIn: "1h", algorithm: "HS256", }; export class AuthService { constructor(options) { // Initialize adapter this.adapter = options.adapter === "in-memory" ? new InMemoryAdapter() : options.adapter; // Validate and configure token options if (!options.token?.secret) { throw new Error("JWT secret is required"); } const { secret, ...jwtOptions } = options.token; this.jwtSecret = secret; this.jwtOptions = { ...DEFAULT_JWT_OPTIONS, ...jwtOptions }; // Configure password policy this.passwordPolicy = options.passwordPolicy; } async register(registrationData) { // Destructure password from registration data const { password, ...accountData } = registrationData; // Check existing account const existingAccount = await this.adapter.getAccount(accountData.id); if (existingAccount) { throw new AccountAlreadyExistsError(accountData.id); } // Validate password policy validatePassword(password, this.passwordPolicy); // Create account object with proper typing const account = { ...accountData, passwordHash: await this.hashPassword(password), createdAt: new Date(), updatedAt: new Date(), }; await this.adapter.addAccount(account); } async authenticate(id, password) { const account = await this.adapter.getAccount(id); if (!account) { throw new AccountNotFoundError(id); } const isValid = await compare(password, account.passwordHash); if (!isValid) { throw new InvalidCredentialsError(); } return this.generateToken(account.id); } async resetPassword(id, newPassword) { validatePassword(newPassword, this.passwordPolicy); const user = await this.adapter.getAccount(id); if (!user) throw new AccountNotFoundError(id); const updatedAccount = { ...user, passwordHash: await this.hashPassword(newPassword), updatedAt: new Date(), }; await this.adapter.updateAccount(updatedAccount); } verifyToken(token) { const payload = verify(token, this.jwtSecret, { algorithms: [this.jwtOptions.algorithm], }); return payload.sub; } async hashPassword(password) { return hash(password, 10); } generateToken(accountId) { return sign({ sub: accountId }, this.jwtSecret, this.jwtOptions); } }