UNPKG

@authxyz/local

Version:

A robust authentication library for express.js

320 lines (319 loc) 16.4 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Local_roles, _Local_adapter, _Local_auth, _Local_mailConfig, _Local_verifications, _Local_resetCodes, _Local_mailClient; import bcrypt from "bcrypt"; import core from "@authxyz/core"; import { asyncHandler, withRequestProcessors } from "./utils.js"; import { registerSchema, resetPasswordSchema } from "./validation.js"; import NodeCache from "node-cache"; import { forgotPasswordSchema, verificationSchema } from "./validation.js"; import { NoSecretTokenError } from "./errors.js"; class Local { constructor({ roles, adapter, auth, mailClient }) { _Local_roles.set(this, void 0); _Local_adapter.set(this, void 0); _Local_auth.set(this, void 0); _Local_mailConfig.set(this, void 0); _Local_verifications.set(this, void 0); _Local_resetCodes.set(this, void 0); _Local_mailClient.set(this, void 0); if (!auth.secret) { throw new NoSecretTokenError("Please provide a secret token for JWT or Cookie authentication."); } __classPrivateFieldSet(this, _Local_roles, roles, "f"); __classPrivateFieldSet(this, _Local_adapter, adapter, "f"); __classPrivateFieldSet(this, _Local_auth, auth, "f"); __classPrivateFieldSet(this, _Local_mailConfig, {}, "f"); __classPrivateFieldSet(this, _Local_mailClient, mailClient, "f"); __classPrivateFieldSet(this, _Local_verifications, new NodeCache({ stdTTL: 60 * 5 * 1000 }), "f"); __classPrivateFieldSet(this, _Local_resetCodes, new NodeCache({ stdTTL: 60 * 5 * 1000 }), "f"); } protect(roles) { return core.middlewares.useProtect(roles, { secret: __classPrivateFieldGet(this, _Local_auth, "f").secret, adapter: __classPrivateFieldGet(this, _Local_adapter, "f"), validationMethod: __classPrivateFieldGet(this, _Local_auth, "f").type, }); } register(path, { role, body, pre, post, }) { const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "POST") { const payload = body ? body(req.body) : registerSchema.parse(req.body); const passwordHash = bcrypt.hashSync(payload["password"], bcrypt.genSaltSync(10)); const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").addUser(Object.assign(Object.assign({}, payload), { password: passwordHash, role: role, verified: false })); if (!user) { return { context: null, resolveMainHandler: () => res.status(500).json({ error: "Internal server error." }), }; } const token = core.sign.signAuth({ method: __classPrivateFieldGet(this, _Local_auth, "f").type, res, data: { id: (user === null || user === void 0 ? void 0 : user.id) || (user === null || user === void 0 ? void 0 : user._id) }, secret: __classPrivateFieldGet(this, _Local_auth, "f").secret, options: { jwtOptions: __classPrivateFieldGet(this, _Local_auth, "f").options, cookieOptions: __classPrivateFieldGet(this, _Local_auth, "f").options, }, }); const verificationCode = Math.ceil(Math.random() * 1000000); this.sendMail("onRegister", { user, verificationCode }); return { context: { token, email: user === null || user === void 0 ? void 0 : user.email, id: (user === null || user === void 0 ? void 0 : user.id) || (user === null || user === void 0 ? void 0 : user._id) }, resolveMainHandler: () => res.status(200).json({ token }), }; } else { next(); } })); return withRequestProcessors(path, { main: handler, post: post, pre: pre, }); } login(path, { role, body, pre, post }) { const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "POST") { const payload = body ? body(req.body) : registerSchema.parse(req.body); const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").getUser({ email: payload["email"] }); if (!user) { return { context: null, resolveMainHandler: () => res.status(401).json({ error: "User not found." }), }; } const isPasswordMatched = bcrypt.compareSync(payload["password"], user["password"]); if (!isPasswordMatched) { return { context: null, resolveMainHandler: () => res.status(401).json({ error: "Invalid login credentials." }), }; } const token = core.sign.signAuth({ method: __classPrivateFieldGet(this, _Local_auth, "f").type, res, data: { id: (user === null || user === void 0 ? void 0 : user.id) || (user === null || user === void 0 ? void 0 : user._id) }, secret: __classPrivateFieldGet(this, _Local_auth, "f").secret, options: { jwtOptions: __classPrivateFieldGet(this, _Local_auth, "f").options, cookieOptions: __classPrivateFieldGet(this, _Local_auth, "f").options, }, }); this.sendMail("onLogin", { user, verificationCode: 0 }); return { context: { user }, resolveMainHandler: () => res.status(200).json({ token }), }; } else { next(); } })); return withRequestProcessors(path, { pre: pre, main: handler, post: post, }); } verify(path, { body, role, post, pre }) { const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "POST") { const payload = body ? body(req.body) : verificationSchema.parse(req.body); const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").getUser({ email: req.body["email"] }); if (!user) { return { context: null, resolveMainHandler: () => res.status(400).json({ error: "User not found." }), }; } const verificationCode = __classPrivateFieldGet(this, _Local_verifications, "f").get(user["id"]); if (verificationCode !== payload["code"]) { return { context: null, resolveMainHandler: () => res.status(400).json({ error: "Invalid verification code." }), }; } yield __classPrivateFieldGet(this, _Local_adapter, "f").updateUser({ id: user["id"] || user["_id"], update: { verified: true }, }); this.sendMail("onVerificationSuccess", { user: Object, verificationCode: payload["code"], }); return { context: { user }, resolveMainHandler: () => res.status(200).json({ message: "OK" }), }; } else { next(); } })); return withRequestProcessors(path, { main: handler, post: post, pre: pre, }); } resendVerification(path, { role, post, pre, }) { const useAuthenticated = core.middlewares.middlewareValidateAuthorization({ secret: __classPrivateFieldGet(this, _Local_auth, "f").secret, method: __classPrivateFieldGet(this, _Local_auth, "f").type, }); const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "GET") { const isAuthenticated = yield useAuthenticated(req, res); if (!isAuthenticated.status) { return { context: null, resolveMainHandler: () => res.status(401).json({ error: "Unauthorized" }), }; } const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").getUser({ id: isAuthenticated.data.id, }); const verificationCode = Math.ceil(Math.random() * 1000000); this.sendMail("onVerificationResend", { user, verificationCode: verificationCode, }); return { context: { user, verificationCode }, resolveMainHandler: () => res.status(200).json({ verificationCode }), }; } })); return withRequestProcessors(path, { pre: pre, post: post, main: handler, }); } forgotPassword(path, { body, role, post, pre, }) { const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "POST") { const payload = body ? body(req.body) : forgotPasswordSchema.parse(req.body); const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").getUser({ email: payload["email"] }); if (!user) { return { context: null, resolveMainHandler: () => res.status(400).json({ error: "User not found." }), }; } const resetCode = Math.ceil(Math.random() * 1000000); this.sendMail("onForgotPassword", { user, verificationCode: resetCode, }); return { context: { user, resetCode }, resolveMainHandler: () => res.status(200).json({ message: "Password reset code sent." }), }; } else { next(); } })); return withRequestProcessors(path, { pre: pre, main: handler, post: post, }); } resetPassword(path, { body, role, post, pre, }) { const handler = asyncHandler((req, res, next) => __awaiter(this, void 0, void 0, function* () { if (req.path === path && req.method === "POST") { const payload = body ? body(req.body) : resetPasswordSchema.parse(req.body); const user = yield __classPrivateFieldGet(this, _Local_adapter, "f").getUser({ email: payload["email"] }); if (!user) { return { context: null, resolveMainHandler: () => res.status(400).json({ error: "User not found." }), }; } const resetCode = __classPrivateFieldGet(this, _Local_resetCodes, "f").get(user["email"]); if (resetCode !== payload["code"]) { return { context: null, resolveMainHandler: () => res.status(400).json({ error: "Invalid reset code." }), }; } const passwordHash = bcrypt.hashSync(payload["password"], bcrypt.genSaltSync(10)); yield __classPrivateFieldGet(this, _Local_adapter, "f").updateUser({ id: user["id"] || user["_id"], update: { password: passwordHash }, }); this.sendMail("onPasswordChange", { user: Object, verificationCode: payload["code"], }); return { context: { email: user["email"], user }, resolveMainHandler: () => res.status(200).json({ message: "Password Changed" }), }; } else { next(); } })); return withRequestProcessors(path, { pre: pre, main: handler, post: post, }); } addTrigger(type, callback) { __classPrivateFieldGet(this, _Local_mailConfig, "f")[type] = callback; } sendMail(type_1, _a) { return __awaiter(this, arguments, void 0, function* (type, { user, verificationCode }) { if (__classPrivateFieldGet(this, _Local_mailConfig, "f")[type]) { const info = yield Promise.resolve(__classPrivateFieldGet(this, _Local_mailConfig, "f")[type]({ user, verificationCode })); if (verificationCode && __classPrivateFieldGet(this, _Local_auth, "f").verification && type === "onRegister") { __classPrivateFieldGet(this, _Local_verifications, "f").set(user["id"] || user["_id"], verificationCode); } if (verificationCode && type === "onForgotPassword") { __classPrivateFieldGet(this, _Local_resetCodes, "f").set(user["email"], verificationCode); } const hello = yield __classPrivateFieldGet(this, _Local_mailClient, "f").sendMail(Object.assign({ to: info.config.to, subject: info.config.subject }, (info.config.type === "html" ? { html: info.config.body } : { text: info.config.body }))); } }); } mailConfig(mailer) { __classPrivateFieldSet(this, _Local_mailClient, mailer, "f"); } } _Local_roles = new WeakMap(), _Local_adapter = new WeakMap(), _Local_auth = new WeakMap(), _Local_mailConfig = new WeakMap(), _Local_verifications = new WeakMap(), _Local_resetCodes = new WeakMap(), _Local_mailClient = new WeakMap(); export default Local;