@authxyz/local
Version:
A robust authentication library for express.js
320 lines (319 loc) • 16.4 kB
JavaScript
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;