UNPKG

@fdm-monster/server

Version:

FDM Monster is a bulk OctoPrint, Klipper, PrusaLink and BambuLab manager to set up, configure and monitor 3D printers. Our aim is to provide neat overview over your farm.

105 lines (104 loc) 4.79 kB
import { AuthenticationError } from "../../exceptions/runtime.exceptions.js"; import { AUTH_ERROR_REASON } from "../../constants/authorization.constants.js"; import { comparePasswordHash } from "../../utils/crypto.utils.js"; import { captureException } from "@sentry/node"; //#region src/services/authentication/auth.service.ts var AuthService = class AuthService { logger; /** * When users are blacklisted at runtime, this cache can make quick work of rejecting them */ blacklistedJwtCache = {}; constructor(loggerFactory, userService, jwtService, settingsStore, refreshTokenService) { this.userService = userService; this.jwtService = jwtService; this.settingsStore = settingsStore; this.refreshTokenService = refreshTokenService; this.logger = loggerFactory(AuthService.name); } async loginUser(username, password) { const userDoc = await this.userService.findRawByUsername(username); if (!userDoc) throw new AuthenticationError("Login incorrect", AUTH_ERROR_REASON.IncorrectCredentials); if (!comparePasswordHash(password, userDoc.passwordHash)) throw new AuthenticationError("Login incorrect", AUTH_ERROR_REASON.IncorrectCredentials); const userId = userDoc.id; const token = await this.signJwtToken(userId); await this.refreshTokenService.purgeOutdatedRefreshTokensByUserId(userId); await this.purgeOutdatedBlacklistedJwtCache(); return { token, refreshToken: await this.refreshTokenService.createRefreshTokenForUserId(userId) }; } async logoutUserId(userId, jwtToken) { await this.refreshTokenService.deleteRefreshTokenByUserId(userId); if (jwtToken?.length) { this.blacklistedJwtCache[jwtToken] = { userId, createdAt: Date.now() }; await this.purgeOutdatedBlacklistedJwtCache(); } } async purgeOutdatedBlacklistedJwtCache() { try { const { jwtExpiresIn } = await this.settingsStore.getCredentialSettings(); const now = Date.now(); const keys = Object.keys(this.blacklistedJwtCache); for (const key of keys) { const { createdAt } = this.blacklistedJwtCache[key]; if (now - createdAt > jwtExpiresIn) delete this.blacklistedJwtCache[key]; } } catch (err) { this.logger.error("Failed to purge blacklisted jwt cache", err); captureException(err); } } async logoutUserRefreshToken(refreshToken) { const userRefreshToken = await this.getValidRefreshToken(refreshToken); await this.refreshTokenService.deleteRefreshTokenByUserId(userRefreshToken.userId); } async renewLoginByRefreshToken(refreshToken) { const userRefreshToken = await this.getValidRefreshToken(refreshToken); const userId = userRefreshToken.userId; if (!await this.userService.getUser(userId)) { await this.refreshTokenService.deleteRefreshToken(refreshToken); throw new AuthenticationError("User not found", AUTH_ERROR_REASON.InvalidOrExpiredRefreshToken); } const token = await this.signJwtToken(userId); await this.increaseRefreshTokenAttemptsUsed(userRefreshToken.refreshToken); return token; } isJwtTokenBlacklisted(jwtToken) { return this.blacklistedJwtCache[jwtToken]; } async getValidRefreshToken(refreshToken) { const userRefreshToken = await this.refreshTokenService.getRefreshToken(refreshToken); if (Date.now() > userRefreshToken.expiresAt) { await this.refreshTokenService.deleteRefreshTokenByUserId(userRefreshToken.userId); throw new AuthenticationError("Refresh token expired, login required", AUTH_ERROR_REASON.InvalidOrExpiredRefreshToken); } return userRefreshToken; } async increaseRefreshTokenAttemptsUsed(refreshToken) { const { refreshTokenAttempts } = await this.settingsStore.getCredentialSettings(); const userRefreshToken = await this.getValidRefreshToken(refreshToken); const attemptsUsed = userRefreshToken.refreshAttemptsUsed; if (refreshTokenAttempts !== -1) { if (attemptsUsed >= refreshTokenAttempts) { await this.refreshTokenService.deleteRefreshTokenByUserId(userRefreshToken.userId); throw new AuthenticationError("Refresh token attempts exceeded, login required", AUTH_ERROR_REASON.InvalidOrExpiredRefreshToken); } } await this.refreshTokenService.updateRefreshTokenAttempts(refreshToken, attemptsUsed + 1); } async signJwtToken(userId) { const user = await this.userService.getUser(userId); if (!user) throw new AuthenticationError("User not found", AUTH_ERROR_REASON.InvalidOrExpiredRefreshToken); if (user.needsPasswordChange) throw new AuthenticationError("Password change required", AUTH_ERROR_REASON.PasswordChangeRequired); if (!user.isVerified) throw new AuthenticationError("User is not verified yet", AUTH_ERROR_REASON.AccountNotVerified); return this.jwtService.signJwtToken(userId, user.username); } }; //#endregion export { AuthService }; //# sourceMappingURL=auth.service.js.map