arkos 0.25.0-beta.1 → 1.0.0-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- "use strict";var __awaiter=this&&this.__awaiter||function(l,e,t,i){function n(r){return r instanceof t?r:new t(function(s){s(r)})}return new(t||(t=Promise))(function(r,s){function a(o){try{u(i.next(o))}catch(d){s(d)}}function c(o){try{u(i.throw(o))}catch(d){s(d)}}function u(o){o.done?r(o.value):n(o.value).then(a,c)}u((i=i.apply(l,e||[])).next())})},__importDefault=this&&this.__importDefault||function(l){return l&&l.__esModule?l:{default:l}};Object.defineProperty(exports,"__esModule",{value:!0});const jsonwebtoken_1=__importDefault(require("jsonwebtoken")),bcryptjs_1=__importDefault(require("bcryptjs")),catch_async_1=__importDefault(require("../error-handler/utils/catch-async")),app_error_1=__importDefault(require("../error-handler/utils/app-error")),base_middlewares_1=require("../base/base.middlewares"),server_1=require("../../server"),arkos_env_1=__importDefault(require("../../utils/arkos-env")),prisma_helpers_1=require("../../utils/helpers/prisma.helpers"),change_case_helpers_1=require("../../utils/helpers/change-case.helpers"),pluralize_1=require("pluralize");class AuthService{constructor(){this.authenticate=(0,catch_async_1.default)((e,t,i)=>__awaiter(this,void 0,void 0,function*(){const n=(0,server_1.getInitConfigs)();if(n?.authentication===!1)return i();e.user=yield this.getAuthenticatedUser(e),i()}))}signJwtToken(e,t=process.env.JWT_EXPIRES_IN||arkos_env_1.default.JWT_EXPIRES_IN,i=process.env.JWT_SECRET||arkos_env_1.default.JWT_SECRET){return jsonwebtoken_1.default.sign({id:e},i,{expiresIn:t})}isCorrectPassword(e,t){return __awaiter(this,void 0,void 0,function*(){return yield bcryptjs_1.default.compare(e,t)})}hashPassword(e){return __awaiter(this,void 0,void 0,function*(){return yield bcryptjs_1.default.hash(e,12)})}isPasswordStrong(e){var t,i;return(((i=((t=(0,server_1.getInitConfigs)())===null||t===void 0?void 0:t.authentication).passwordValidation)===null||i===void 0?void 0:i.regex)||/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).+$/).test(e)}userChangedPasswordAfter(e,t){if(e.passwordChangedAt){const i=parseInt(String(e.passwordChangedAt.getTime()/1e3),10);return t<i}return!1}verifyJwtToken(e){return __awaiter(this,arguments,void 0,function*(t,i=process.env.JWT_SECRET||arkos_env_1.default.JWT_SECRET){return new Promise((n,r)=>{jsonwebtoken_1.default.verify(t,i,(s,a)=>{s?r(s):n(a)})})})}handleActionAccessControl(e,t,i){return(0,catch_async_1.default)((n,r,s)=>__awaiter(this,void 0,void 0,function*(){if(n.user){const a=n.user;if(!(yield(0,prisma_helpers_1.getPrismaInstance)().authPermission.count({where:{resource:{equals:(0,change_case_helpers_1.kebabCase)((0,pluralize_1.singular)(i)),mode:"insensitive"},action:t,roleId:{in:a.roles.map(o=>o.roleId)}}})))return s(new app_error_1.default("You do not have permission to perfom this action",403))}s()}))}getAuthenticatedUser(e){return __awaiter(this,void 0,void 0,function*(){var t,i,n,r,s;const a=(0,server_1.getInitConfigs)();if(a?.authentication===!1)return null;const c=(0,prisma_helpers_1.getPrismaInstance)();let u;if(!((t=e?.headers)===null||t===void 0)&&t.authorization&&(!((i=e?.headers)===null||i===void 0)&&i.authorization.startsWith("Bearer"))?u=(n=e?.headers)===null||n===void 0?void 0:n.authorization.split(" ")[1]:((r=e?.cookies)===null||r===void 0?void 0:r.arkos_access_token)!=="no-token"&&e.cookies&&(u=(s=e?.cookies)===null||s===void 0?void 0:s.arkos_access_token),!u)throw new app_error_1.default("You are not logged in! please log in to get access",401);let o;try{o=yield this.verifyJwtToken(u)}catch{throw new app_error_1.default("Your auth token is invalid, please login again.",401)}if(!o?.id)throw new app_error_1.default("Your auth token is invalid, please login again.",401);const d=yield c.user.findUnique({where:{id:String(o.id)},include:{roles:{include:{role:{include:{permissions:!0}}}}}});if(!d)throw new app_error_1.default("The user belonging to this token does no longer exists",401);if(this.userChangedPasswordAfter(d,o.iat)&&!e.path.includes("logout"))throw new app_error_1.default("User recently changed password! Please log in again.",401);return d})}handleAuthenticationControl(e,t,i){const n=e?.authenticationControl;if(n&&typeof n=="object"){if(n[t]===!1)return base_middlewares_1.callNext;if(n[t]===!0)return this.authenticate}else return this.authenticate;return this.authenticate}}const authService=new AuthService;exports.default=authService;
1
+ "use strict";var __awaiter=this&&this.__awaiter||function(l,e,t,i){function n(r){return r instanceof t?r:new t(function(s){s(r)})}return new(t||(t=Promise))(function(r,s){function a(o){try{u(i.next(o))}catch(d){s(d)}}function c(o){try{u(i.throw(o))}catch(d){s(d)}}function u(o){o.done?r(o.value):n(o.value).then(a,c)}u((i=i.apply(l,e||[])).next())})},__importDefault=this&&this.__importDefault||function(l){return l&&l.__esModule?l:{default:l}};Object.defineProperty(exports,"__esModule",{value:!0});const jsonwebtoken_1=__importDefault(require("jsonwebtoken")),bcryptjs_1=__importDefault(require("bcryptjs")),catch_async_1=__importDefault(require("../error-handler/utils/catch-async")),app_error_1=__importDefault(require("../error-handler/utils/app-error")),base_middlewares_1=require("../base/base.middlewares"),server_1=require("../../server"),arkos_env_1=__importDefault(require("../../utils/arkos-env")),prisma_helpers_1=require("../../utils/helpers/prisma.helpers"),change_case_helpers_1=require("../../utils/helpers/change-case.helpers"),pluralize_1=require("pluralize");class AuthService{constructor(){this.authenticate=(0,catch_async_1.default)((e,t,i)=>__awaiter(this,void 0,void 0,function*(){const n=(0,server_1.getInitConfigs)();if(n?.authentication===!1)return i();e.user=yield this.getAuthenticatedUser(e),i()}))}signJwtToken(e,t=process.env.JWT_EXPIRES_IN||arkos_env_1.default.JWT_EXPIRES_IN,i=process.env.JWT_SECRET||arkos_env_1.default.JWT_SECRET){return jsonwebtoken_1.default.sign({id:e},i,{expiresIn:t})}isCorrectPassword(e,t){return __awaiter(this,void 0,void 0,function*(){return yield bcryptjs_1.default.compare(e,t)})}hashPassword(e){return __awaiter(this,void 0,void 0,function*(){return yield bcryptjs_1.default.hash(e,12)})}isPasswordStrong(e){var t,i;return(((i=((t=(0,server_1.getInitConfigs)())===null||t===void 0?void 0:t.authentication).passwordValidation)===null||i===void 0?void 0:i.regex)||/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).+$/).test(e)}userChangedPasswordAfter(e,t){if(e.passwordChangedAt){const i=parseInt(String(e.passwordChangedAt.getTime()/1e3),10);return t<i}return!1}verifyJwtToken(e){return __awaiter(this,arguments,void 0,function*(t,i=process.env.JWT_SECRET||arkos_env_1.default.JWT_SECRET){return new Promise((n,r)=>{jsonwebtoken_1.default.verify(t,i,(s,a)=>{s?r(s):n(a)})})})}handleActionAccessControl(e,t,i){return(0,catch_async_1.default)((n,r,s)=>__awaiter(this,void 0,void 0,function*(){if(n.user){const a=n.user;if(!(yield(0,prisma_helpers_1.getPrismaInstance)().authPermission.count({where:{resource:(0,change_case_helpers_1.kebabCase)((0,pluralize_1.singular)(i)),action:t,roleId:{in:a.roles.map(o=>o.roleId)}}})))return s(new app_error_1.default("You do not have permission to perfom this action",403))}s()}))}getAuthenticatedUser(e){return __awaiter(this,void 0,void 0,function*(){var t,i,n,r,s;const a=(0,server_1.getInitConfigs)();if(a?.authentication===!1)return null;const c=(0,prisma_helpers_1.getPrismaInstance)();let u;if(!((t=e?.headers)===null||t===void 0)&&t.authorization&&(!((i=e?.headers)===null||i===void 0)&&i.authorization.startsWith("Bearer"))?u=(n=e?.headers)===null||n===void 0?void 0:n.authorization.split(" ")[1]:((r=e?.cookies)===null||r===void 0?void 0:r.arkos_access_token)!=="no-token"&&e.cookies&&(u=(s=e?.cookies)===null||s===void 0?void 0:s.arkos_access_token),!u)throw new app_error_1.default("You are not logged in! please log in to get access",401);let o;try{o=yield this.verifyJwtToken(u)}catch{throw new app_error_1.default("Your auth token is invalid, please login again.",401)}if(!o?.id)throw new app_error_1.default("Your auth token is invalid, please login again.",401);const d=yield c.user.findUnique({where:{id:String(o.id)},include:{roles:{include:{role:{include:{permissions:!0}}}}}});if(!d)throw new app_error_1.default("The user belonging to this token does no longer exists",401);if(this.userChangedPasswordAfter(d,o.iat)&&!e.path.includes("logout"))throw new app_error_1.default("User recently changed password! Please log in again.",401);return d})}handleAuthenticationControl(e,t,i){const n=e?.authenticationControl;if(n&&typeof n=="object"){if(n[t]===!1)return base_middlewares_1.callNext;if(n[t]===!0)return this.authenticate}else return this.authenticate;return this.authenticate}}const authService=new AuthService;exports.default=authService;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport bcrypt from \"bcryptjs\";\r\nimport { User, UserRole } from \"../../types\";\r\nimport catchAsync from \"../error-handler/utils/catch-async\";\r\nimport AppError from \"../error-handler/utils/app-error\";\r\nimport { callNext } from \"../base/base.middlewares\";\r\nimport { getInitConfigs } from \"../../server\";\r\nimport arkosEnv from \"../../utils/arkos-env\";\r\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\r\nimport {\r\n ArkosRequest,\r\n ArkosResponse,\r\n ArkosNextFunction,\r\n ArkosRequestHandler,\r\n} from \"../../types\";\r\nimport {\r\n AuthConfigs,\r\n AuthJwtPayload,\r\n ControllerActions,\r\n} from \"../../types/auth\";\r\nimport { InitConfigsAuthenticationOptions } from \"../../types/init-configs\";\r\nimport { kebabCase } from \"../../utils/helpers/change-case.helpers\";\r\nimport { singular } from \"pluralize\";\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nclass AuthService {\r\n /**\r\n * Signs a JWT token for the user.\r\n *\r\n * @param {number | string} id - The unique identifier of the user to generate the token for.\r\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\r\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {string} The signed JWT token.\r\n */\r\n signJwtToken(\r\n id: number | string,\r\n expiresIn: string | number = process.env.JWT_EXPIRES_IN ||\r\n arkosEnv.JWT_EXPIRES_IN,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): string {\r\n return jwt.sign({ id }, secret, {\r\n expiresIn: expiresIn as any,\r\n });\r\n }\r\n\r\n /**\r\n * Compares a candidate password with the stored user password to check if they match.\r\n *\r\n * @param {string} candidatePassword - The password provided by the user during login.\r\n * @param {string} userPassword - The password stored in the database.\r\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\r\n */\r\n async isCorrectPassword(\r\n candidatePassword: string,\r\n userPassword: string\r\n ): Promise<boolean> {\r\n return await bcrypt.compare(candidatePassword, userPassword);\r\n }\r\n\r\n /**\r\n * Hashes a plain text password using bcrypt.\r\n *\r\n * @param {string} password - The password to be hashed.\r\n * @returns {Promise<string>} Returns the hashed password.\r\n */\r\n async hashPassword(password: string): Promise<string> {\r\n return await bcrypt.hash(password, 12);\r\n }\r\n\r\n /**\r\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\r\n *\r\n * **Note**: You can define it when calling arkos.init()\r\n * ```ts\r\n * arkos.init(app, {\r\n * authentication: {\r\n * passwordRegex: /your-desired-regex/\r\n * }\r\n * })\r\n * ```\r\n *\r\n * @param {string} password - The password to check.\r\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\r\n */\r\n isPasswordStrong(password: string): boolean {\r\n const initAuthConfigs = getInitConfigs()\r\n ?.authentication as InitConfigsAuthenticationOptions;\r\n\r\n const strongPasswordRegex =\r\n initAuthConfigs.passwordValidation?.regex ||\r\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\r\n return strongPasswordRegex.test(password);\r\n }\r\n\r\n /**\r\n * Checks if a user has changed their password after the JWT was issued.\r\n *\r\n * @param {User} user - The user object containing the passwordChangedAt field.\r\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\r\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\r\n */\r\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\r\n if (user.passwordChangedAt) {\r\n const convertedTimestamp = parseInt(\r\n String(user.passwordChangedAt.getTime() / 1000),\r\n 10\r\n );\r\n\r\n return JWTTimestamp < convertedTimestamp;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Verifies the authenticity of a JWT token.\r\n *\r\n * @param {string} token - The JWT token to verify.\r\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\r\n * @throws {Error} Throws an error if the token is invalid or expired.\r\n */\r\n async verifyJwtToken(\r\n token: string,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): Promise<AuthJwtPayload> {\r\n return new Promise((resolve, reject) => {\r\n jwt.verify(token, secret, (err, decoded) => {\r\n if (err) reject(err);\r\n else resolve(decoded as AuthJwtPayload);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Middleware function to handle access control based on user roles and permissions.\r\n *\r\n * @param {AuthConfigs} authConfigs - The configuration object for authentication and access control.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name that the action is being performed on (e.g., \"User\", \"Post\").\r\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\r\n */\r\n handleActionAccessControl(\r\n authConfigs: AuthConfigs,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n return catchAsync(\r\n async (\r\n req: ArkosRequest,\r\n res: ArkosResponse,\r\n next: ArkosNextFunction\r\n ) => {\r\n if (req.user) {\r\n const user = req.user as any;\r\n const prisma = getPrismaInstance();\r\n\r\n const permissions = await (prisma as any).authPermission.count({\r\n where: {\r\n resource: {\r\n equals: kebabCase(singular(modelName)),\r\n mode: \"insensitive\",\r\n },\r\n action,\r\n roleId: { in: user.roles.map((role: UserRole) => role.roleId) },\r\n },\r\n });\r\n\r\n if (!permissions) {\r\n return next(\r\n new AppError(\r\n \"You do not have permission to perfom this action\",\r\n 403\r\n )\r\n );\r\n }\r\n }\r\n\r\n next();\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * Processes the cookies or authoriation token and returns the user.\r\n * @param req\r\n * @returns {Promise<User | null>} - if authentication is turned off in initConfigs it returns null\r\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\r\n */\r\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return null;\r\n\r\n const prisma = getPrismaInstance();\r\n\r\n let token: string | undefined;\r\n\r\n if (\r\n req?.headers?.authorization &&\r\n req?.headers?.authorization.startsWith(\"Bearer\")\r\n ) {\r\n token = req?.headers?.authorization.split(\" \")[1];\r\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\r\n token = req?.cookies?.arkos_access_token;\r\n }\r\n\r\n if (!token)\r\n throw new AppError(\r\n \"You are not logged in! please log in to get access\",\r\n 401\r\n );\r\n\r\n let decoded: AuthJwtPayload | undefined;\r\n try {\r\n decoded = await this.verifyJwtToken(token);\r\n } catch (err) {\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n }\r\n\r\n if (!decoded?.id)\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n\r\n const user: any | null = await (prisma as any).user.findUnique({\r\n where: { id: String(decoded.id) },\r\n include: {\r\n roles: {\r\n include: {\r\n role: {\r\n include: {\r\n permissions: true,\r\n },\r\n },\r\n },\r\n },\r\n },\r\n });\r\n\r\n if (!user)\r\n throw new AppError(\r\n \"The user belonging to this token does no longer exists\",\r\n 401\r\n );\r\n\r\n if (\r\n this.userChangedPasswordAfter(user, decoded.iat!) &&\r\n !req.path.includes(\"logout\")\r\n )\r\n throw new AppError(\r\n \"User recently changed password! Please log in again.\",\r\n 401\r\n );\r\n\r\n return user;\r\n }\r\n\r\n /**\r\n * Middleware function to authenticate the user based on the JWT token.\r\n *\r\n * @param {ArkosRequest} req - The request object.\r\n * @param {ArkosResponse} res - The response object.\r\n * @param {ArkosNextFunction} next - The next middleware function to be called.\r\n * @returns {void}\r\n */\r\n authenticate = catchAsync(\r\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return next();\r\n\r\n req.user = (await this.getAuthenticatedUser(req)) as User;\r\n next();\r\n }\r\n );\r\n\r\n /**\r\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\r\n *\r\n * @param {AuthConfigs | undefined} authConfigs - The authentication configuration object.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name being affected by the action.\r\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\r\n */\r\n handleAuthenticationControl(\r\n authConfigs: AuthConfigs | undefined,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n const authenticationControl = authConfigs?.authenticationControl;\r\n\r\n if (authenticationControl && typeof authenticationControl === \"object\") {\r\n if (authenticationControl[action] === false) return callNext;\r\n else if (authenticationControl[action] === true) return this.authenticate;\r\n } else return this.authenticate;\r\n\r\n return this.authenticate;\r\n }\r\n}\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nconst authService = new AuthService();\r\n\r\nexport default authService;\r\n"]}
1
+ {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport bcrypt from \"bcryptjs\";\r\nimport { User, UserRole } from \"../../types\";\r\nimport catchAsync from \"../error-handler/utils/catch-async\";\r\nimport AppError from \"../error-handler/utils/app-error\";\r\nimport { callNext } from \"../base/base.middlewares\";\r\nimport { getInitConfigs } from \"../../server\";\r\nimport arkosEnv from \"../../utils/arkos-env\";\r\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\r\nimport {\r\n ArkosRequest,\r\n ArkosResponse,\r\n ArkosNextFunction,\r\n ArkosRequestHandler,\r\n} from \"../../types\";\r\nimport {\r\n AuthConfigs,\r\n AuthJwtPayload,\r\n ControllerActions,\r\n} from \"../../types/auth\";\r\nimport { InitConfigsAuthenticationOptions } from \"../../types/init-configs\";\r\nimport { kebabCase } from \"../../utils/helpers/change-case.helpers\";\r\nimport { singular } from \"pluralize\";\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nclass AuthService {\r\n /**\r\n * Signs a JWT token for the user.\r\n *\r\n * @param {number | string} id - The unique identifier of the user to generate the token for.\r\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\r\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {string} The signed JWT token.\r\n */\r\n signJwtToken(\r\n id: number | string,\r\n expiresIn: string | number = process.env.JWT_EXPIRES_IN ||\r\n arkosEnv.JWT_EXPIRES_IN,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): string {\r\n return jwt.sign({ id }, secret, {\r\n expiresIn: expiresIn as any,\r\n });\r\n }\r\n\r\n /**\r\n * Compares a candidate password with the stored user password to check if they match.\r\n *\r\n * @param {string} candidatePassword - The password provided by the user during login.\r\n * @param {string} userPassword - The password stored in the database.\r\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\r\n */\r\n async isCorrectPassword(\r\n candidatePassword: string,\r\n userPassword: string\r\n ): Promise<boolean> {\r\n return await bcrypt.compare(candidatePassword, userPassword);\r\n }\r\n\r\n /**\r\n * Hashes a plain text password using bcrypt.\r\n *\r\n * @param {string} password - The password to be hashed.\r\n * @returns {Promise<string>} Returns the hashed password.\r\n */\r\n async hashPassword(password: string): Promise<string> {\r\n return await bcrypt.hash(password, 12);\r\n }\r\n\r\n /**\r\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\r\n *\r\n * **Note**: You can define it when calling arkos.init()\r\n * ```ts\r\n * arkos.init({\r\n * authentication: {\r\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\r\n * }\r\n * })\r\n * ```\r\n *\r\n * @param {string} password - The password to check.\r\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\r\n */\r\n isPasswordStrong(password: string): boolean {\r\n const initAuthConfigs = getInitConfigs()\r\n ?.authentication as InitConfigsAuthenticationOptions;\r\n\r\n const strongPasswordRegex =\r\n initAuthConfigs.passwordValidation?.regex ||\r\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\r\n return strongPasswordRegex.test(password);\r\n }\r\n\r\n /**\r\n * Checks if a user has changed their password after the JWT was issued.\r\n *\r\n * @param {User} user - The user object containing the passwordChangedAt field.\r\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\r\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\r\n */\r\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\r\n if (user.passwordChangedAt) {\r\n const convertedTimestamp = parseInt(\r\n String(user.passwordChangedAt.getTime() / 1000),\r\n 10\r\n );\r\n\r\n return JWTTimestamp < convertedTimestamp;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Verifies the authenticity of a JWT token.\r\n *\r\n * @param {string} token - The JWT token to verify.\r\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\r\n * @throws {Error} Throws an error if the token is invalid or expired.\r\n */\r\n async verifyJwtToken(\r\n token: string,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): Promise<AuthJwtPayload> {\r\n return new Promise((resolve, reject) => {\r\n jwt.verify(token, secret, (err, decoded) => {\r\n if (err) reject(err);\r\n else resolve(decoded as AuthJwtPayload);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Middleware function to handle access control based on user roles and permissions.\r\n *\r\n * @param {AuthConfigs} authConfigs - The configuration object for authentication and access control.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name that the action is being performed on (e.g., \"User\", \"Post\").\r\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\r\n */\r\n handleActionAccessControl(\r\n authConfigs: AuthConfigs,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n return catchAsync(\r\n async (\r\n req: ArkosRequest,\r\n res: ArkosResponse,\r\n next: ArkosNextFunction\r\n ) => {\r\n if (req.user) {\r\n const user = req.user as any;\r\n const prisma = getPrismaInstance();\r\n\r\n const permissions = await (prisma as any).authPermission.count({\r\n where: {\r\n resource: kebabCase(singular(modelName)),\r\n action,\r\n roleId: { in: user.roles.map((role: UserRole) => role.roleId) },\r\n },\r\n });\r\n\r\n if (!permissions) {\r\n return next(\r\n new AppError(\r\n \"You do not have permission to perfom this action\",\r\n 403\r\n )\r\n );\r\n }\r\n }\r\n\r\n next();\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * Processes the cookies or authoriation token and returns the user.\r\n * @param req\r\n * @returns {Promise<User | null>} - if authentication is turned off in initConfigs it returns null\r\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\r\n */\r\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return null;\r\n\r\n const prisma = getPrismaInstance();\r\n\r\n let token: string | undefined;\r\n\r\n if (\r\n req?.headers?.authorization &&\r\n req?.headers?.authorization.startsWith(\"Bearer\")\r\n ) {\r\n token = req?.headers?.authorization.split(\" \")[1];\r\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\r\n token = req?.cookies?.arkos_access_token;\r\n }\r\n\r\n if (!token)\r\n throw new AppError(\r\n \"You are not logged in! please log in to get access\",\r\n 401\r\n );\r\n\r\n let decoded: AuthJwtPayload | undefined;\r\n try {\r\n decoded = await this.verifyJwtToken(token);\r\n } catch (err) {\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n }\r\n\r\n if (!decoded?.id)\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n\r\n const user: any | null = await (prisma as any).user.findUnique({\r\n where: { id: String(decoded.id) },\r\n include: {\r\n roles: {\r\n include: {\r\n role: {\r\n include: {\r\n permissions: true,\r\n },\r\n },\r\n },\r\n },\r\n },\r\n });\r\n\r\n if (!user)\r\n throw new AppError(\r\n \"The user belonging to this token does no longer exists\",\r\n 401\r\n );\r\n\r\n if (\r\n this.userChangedPasswordAfter(user, decoded.iat!) &&\r\n !req.path.includes(\"logout\")\r\n )\r\n throw new AppError(\r\n \"User recently changed password! Please log in again.\",\r\n 401\r\n );\r\n\r\n return user;\r\n }\r\n\r\n /**\r\n * Middleware function to authenticate the user based on the JWT token.\r\n *\r\n * @param {ArkosRequest} req - The request object.\r\n * @param {ArkosResponse} res - The response object.\r\n * @param {ArkosNextFunction} next - The next middleware function to be called.\r\n * @returns {void}\r\n */\r\n authenticate = catchAsync(\r\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return next();\r\n\r\n req.user = (await this.getAuthenticatedUser(req)) as User;\r\n next();\r\n }\r\n );\r\n\r\n /**\r\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\r\n *\r\n * @param {AuthConfigs | undefined} authConfigs - The authentication configuration object.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name being affected by the action.\r\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\r\n */\r\n handleAuthenticationControl(\r\n authConfigs: AuthConfigs | undefined,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n const authenticationControl = authConfigs?.authenticationControl;\r\n\r\n if (authenticationControl && typeof authenticationControl === \"object\") {\r\n if (authenticationControl[action] === false) return callNext;\r\n else if (authenticationControl[action] === true) return this.authenticate;\r\n } else return this.authenticate;\r\n\r\n return this.authenticate;\r\n }\r\n}\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nconst authService = new AuthService();\r\n\r\nexport default authService;\r\n"]}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRelationFieldsInBody=handleRelationFieldsInBody;const models_helpers_1=require("../../../utils/helpers/models.helpers");function handleRelationFieldsInBody(i,p,t=[]){let l={...i};return p.list.forEach(e=>{if(!i[e.name])return;const s=[],a=[],o=[],c=[],r=[];i[e.name].forEach(n=>{if(!t.includes(n?.apiAction))if(n?.apiAction==="delete")r.push(n.id);else if(n?.apiAction==="disconnect")c.push({id:n.id});else if(n?.id)if(Object.keys(n).length===1)a.push(n);else{const{id:d,...h}=n;let u=(0,models_helpers_1.getPrismaModelRelations)(e.type);const m=u?handleRelationFieldsInBody(h,u,t):n;o.push({where:{id:d},data:m})}else{let d=(0,models_helpers_1.getPrismaModelRelations)(e.type);const h=d?handleRelationFieldsInBody(n,d,t):n;s.push(h)}}),l[e.name]={...s.length?{create:s}:{},...a.length?{connect:a}:{},...o.length?{update:o}:{},...c.length?{disconnect:c}:{},...r.length?{deleteMany:{id:{in:r}}}:{}}}),p.singular.forEach(e=>{if(!i[e.name]||t.includes(i[e.name]?.apiAction))return;let s=(0,models_helpers_1.getPrismaModelRelations)(e.type);if(!i[e.name]?.id)l[e.name]={create:handleRelationFieldsInBody(i[e.name],s,t)};else if(Object.keys(i[e.name]).length===1)l[e.name]={connect:i[e.name]};else{const{id:a,...o}=i[e.name];l[e.name]={update:{where:{id:a},data:handleRelationFieldsInBody(o,s,t)}}}}),l}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.handleRelationFieldsInBody=handleRelationFieldsInBody;const models_helpers_1=require("../../../utils/helpers/models.helpers");function handleRelationFieldsInBody(n,p,t=[]){let a={...n};return p.list.forEach(e=>{if(!n[e.name])return;const l=[],s=[],o=[],c=[],r=[];n[e.name].forEach(i=>{if(!t.includes(i?.apiAction))if(i?.apiAction==="delete")r.push(i.id);else if(i?.apiAction==="disconnect")c.push({id:i.id});else if(i?.id)if(Object.keys(i).length===1)s.push(i);else{const{id:d,...h}=i;let m=(0,models_helpers_1.getPrismaModelRelations)(e.type);const u=m?handleRelationFieldsInBody(h,m,t):i;o.push({where:{id:d},data:u})}else{let d=(0,models_helpers_1.getPrismaModelRelations)(e.type);const h=d?handleRelationFieldsInBody(i,d,t):i;l.push(h)}}),a[e.name]={...l.length?{create:l}:{},...s.length?{connect:s}:{},...o.length?{update:o}:{},...c.length?{disconnect:c}:{},...r.length?{deleteMany:{id:{in:r}}}:{}}}),p.singular.forEach(e=>{if(!n[e.name]||t.includes(n[e.name]?.apiAction))return;let l=(0,models_helpers_1.getPrismaModelRelations)(e.type);if(!n[e.name]?.id)a[e.name]={create:handleRelationFieldsInBody(n[e.name],l,t)};else if(Object.keys(n[e.name]).length===1)a[e.name]={connect:n[e.name]};else{const{id:s,...o}=n[e.name];a[e.name]={update:{where:{id:s},data:handleRelationFieldsInBody(o,l,t)}}}}),a}
@@ -1 +1 @@
1
- "use strict";var l=function(p,i,t,o){function e(r){return r instanceof t?r:new t(function(s){s(r)})}return new(t||(t=Promise))(function(r,s){function a(n){try{d(o.next(n))}catch(u){s(u)}}function h(n){try{d(o.throw(n))}catch(u){s(u)}}function d(n){n.done?r(n.value):e(n.value).then(a,h)}d((o=o.apply(p,i||[])).next())})};import m from"jsonwebtoken";import g from"bcryptjs";import w from"../error-handler/utils/catch-async";import c from"../error-handler/utils/app-error";import{callNext as k}from"../base/base.middlewares";import{getInitConfigs as v}from"../../server";import f from"../../utils/arkos-env";import{getPrismaInstance as _}from"../../utils/helpers/prisma.helpers";import{kebabCase as C}from"../../utils/helpers/change-case.helpers";import{singular as y}from"pluralize";class T{constructor(){this.authenticate=w((i,t,o)=>l(this,void 0,void 0,function*(){const e=v();if(e?.authentication===!1)return o();i.user=yield this.getAuthenticatedUser(i),o()}))}signJwtToken(i,t=process.env.JWT_EXPIRES_IN||f.JWT_EXPIRES_IN,o=process.env.JWT_SECRET||f.JWT_SECRET){return m.sign({id:i},o,{expiresIn:t})}isCorrectPassword(i,t){return l(this,void 0,void 0,function*(){return yield g.compare(i,t)})}hashPassword(i){return l(this,void 0,void 0,function*(){return yield g.hash(i,12)})}isPasswordStrong(i){var t,o;return(((o=((t=v())===null||t===void 0?void 0:t.authentication).passwordValidation)===null||o===void 0?void 0:o.regex)||/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).+$/).test(i)}userChangedPasswordAfter(i,t){if(i.passwordChangedAt){const o=parseInt(String(i.passwordChangedAt.getTime()/1e3),10);return t<o}return!1}verifyJwtToken(i){return l(this,arguments,void 0,function*(t,o=process.env.JWT_SECRET||f.JWT_SECRET){return new Promise((e,r)=>{m.verify(t,o,(s,a)=>{s?r(s):e(a)})})})}handleActionAccessControl(i,t,o){return w((e,r,s)=>l(this,void 0,void 0,function*(){if(e.user){const a=e.user;if(!(yield _().authPermission.count({where:{resource:{equals:C(y(o)),mode:"insensitive"},action:t,roleId:{in:a.roles.map(n=>n.roleId)}}})))return s(new c("You do not have permission to perfom this action",403))}s()}))}getAuthenticatedUser(i){return l(this,void 0,void 0,function*(){var t,o,e,r,s;const a=v();if(a?.authentication===!1)return null;const h=_();let d;if(!((t=i?.headers)===null||t===void 0)&&t.authorization&&(!((o=i?.headers)===null||o===void 0)&&o.authorization.startsWith("Bearer"))?d=(e=i?.headers)===null||e===void 0?void 0:e.authorization.split(" ")[1]:((r=i?.cookies)===null||r===void 0?void 0:r.arkos_access_token)!=="no-token"&&i.cookies&&(d=(s=i?.cookies)===null||s===void 0?void 0:s.arkos_access_token),!d)throw new c("You are not logged in! please log in to get access",401);let n;try{n=yield this.verifyJwtToken(d)}catch{throw new c("Your auth token is invalid, please login again.",401)}if(!n?.id)throw new c("Your auth token is invalid, please login again.",401);const u=yield h.user.findUnique({where:{id:String(n.id)},include:{roles:{include:{role:{include:{permissions:!0}}}}}});if(!u)throw new c("The user belonging to this token does no longer exists",401);if(this.userChangedPasswordAfter(u,n.iat)&&!i.path.includes("logout"))throw new c("User recently changed password! Please log in again.",401);return u})}handleAuthenticationControl(i,t,o){const e=i?.authenticationControl;if(e&&typeof e=="object"){if(e[t]===!1)return k;if(e[t]===!0)return this.authenticate}else return this.authenticate;return this.authenticate}}const A=new T;export default A;
1
+ "use strict";var l=function(p,i,t,o){function e(r){return r instanceof t?r:new t(function(s){s(r)})}return new(t||(t=Promise))(function(r,s){function a(n){try{d(o.next(n))}catch(u){s(u)}}function h(n){try{d(o.throw(n))}catch(u){s(u)}}function d(n){n.done?r(n.value):e(n.value).then(a,h)}d((o=o.apply(p,i||[])).next())})};import m from"jsonwebtoken";import g from"bcryptjs";import w from"../error-handler/utils/catch-async";import c from"../error-handler/utils/app-error";import{callNext as k}from"../base/base.middlewares";import{getInitConfigs as v}from"../../server";import f from"../../utils/arkos-env";import{getPrismaInstance as _}from"../../utils/helpers/prisma.helpers";import{kebabCase as C}from"../../utils/helpers/change-case.helpers";import{singular as y}from"pluralize";class T{constructor(){this.authenticate=w((i,t,o)=>l(this,void 0,void 0,function*(){const e=v();if(e?.authentication===!1)return o();i.user=yield this.getAuthenticatedUser(i),o()}))}signJwtToken(i,t=process.env.JWT_EXPIRES_IN||f.JWT_EXPIRES_IN,o=process.env.JWT_SECRET||f.JWT_SECRET){return m.sign({id:i},o,{expiresIn:t})}isCorrectPassword(i,t){return l(this,void 0,void 0,function*(){return yield g.compare(i,t)})}hashPassword(i){return l(this,void 0,void 0,function*(){return yield g.hash(i,12)})}isPasswordStrong(i){var t,o;return(((o=((t=v())===null||t===void 0?void 0:t.authentication).passwordValidation)===null||o===void 0?void 0:o.regex)||/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).+$/).test(i)}userChangedPasswordAfter(i,t){if(i.passwordChangedAt){const o=parseInt(String(i.passwordChangedAt.getTime()/1e3),10);return t<o}return!1}verifyJwtToken(i){return l(this,arguments,void 0,function*(t,o=process.env.JWT_SECRET||f.JWT_SECRET){return new Promise((e,r)=>{m.verify(t,o,(s,a)=>{s?r(s):e(a)})})})}handleActionAccessControl(i,t,o){return w((e,r,s)=>l(this,void 0,void 0,function*(){if(e.user){const a=e.user;if(!(yield _().authPermission.count({where:{resource:C(y(o)),action:t,roleId:{in:a.roles.map(n=>n.roleId)}}})))return s(new c("You do not have permission to perfom this action",403))}s()}))}getAuthenticatedUser(i){return l(this,void 0,void 0,function*(){var t,o,e,r,s;const a=v();if(a?.authentication===!1)return null;const h=_();let d;if(!((t=i?.headers)===null||t===void 0)&&t.authorization&&(!((o=i?.headers)===null||o===void 0)&&o.authorization.startsWith("Bearer"))?d=(e=i?.headers)===null||e===void 0?void 0:e.authorization.split(" ")[1]:((r=i?.cookies)===null||r===void 0?void 0:r.arkos_access_token)!=="no-token"&&i.cookies&&(d=(s=i?.cookies)===null||s===void 0?void 0:s.arkos_access_token),!d)throw new c("You are not logged in! please log in to get access",401);let n;try{n=yield this.verifyJwtToken(d)}catch{throw new c("Your auth token is invalid, please login again.",401)}if(!n?.id)throw new c("Your auth token is invalid, please login again.",401);const u=yield h.user.findUnique({where:{id:String(n.id)},include:{roles:{include:{role:{include:{permissions:!0}}}}}});if(!u)throw new c("The user belonging to this token does no longer exists",401);if(this.userChangedPasswordAfter(u,n.iat)&&!i.path.includes("logout"))throw new c("User recently changed password! Please log in again.",401);return u})}handleAuthenticationControl(i,t,o){const e=i?.authenticationControl;if(e&&typeof e=="object"){if(e[t]===!1)return k;if(e[t]===!0)return this.authenticate}else return this.authenticate;return this.authenticate}}const A=new T;export default A;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport bcrypt from \"bcryptjs\";\r\nimport { User, UserRole } from \"../../types\";\r\nimport catchAsync from \"../error-handler/utils/catch-async\";\r\nimport AppError from \"../error-handler/utils/app-error\";\r\nimport { callNext } from \"../base/base.middlewares\";\r\nimport { getInitConfigs } from \"../../server\";\r\nimport arkosEnv from \"../../utils/arkos-env\";\r\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\r\nimport {\r\n ArkosRequest,\r\n ArkosResponse,\r\n ArkosNextFunction,\r\n ArkosRequestHandler,\r\n} from \"../../types\";\r\nimport {\r\n AuthConfigs,\r\n AuthJwtPayload,\r\n ControllerActions,\r\n} from \"../../types/auth\";\r\nimport { InitConfigsAuthenticationOptions } from \"../../types/init-configs\";\r\nimport { kebabCase } from \"../../utils/helpers/change-case.helpers\";\r\nimport { singular } from \"pluralize\";\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nclass AuthService {\r\n /**\r\n * Signs a JWT token for the user.\r\n *\r\n * @param {number | string} id - The unique identifier of the user to generate the token for.\r\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\r\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {string} The signed JWT token.\r\n */\r\n signJwtToken(\r\n id: number | string,\r\n expiresIn: string | number = process.env.JWT_EXPIRES_IN ||\r\n arkosEnv.JWT_EXPIRES_IN,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): string {\r\n return jwt.sign({ id }, secret, {\r\n expiresIn: expiresIn as any,\r\n });\r\n }\r\n\r\n /**\r\n * Compares a candidate password with the stored user password to check if they match.\r\n *\r\n * @param {string} candidatePassword - The password provided by the user during login.\r\n * @param {string} userPassword - The password stored in the database.\r\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\r\n */\r\n async isCorrectPassword(\r\n candidatePassword: string,\r\n userPassword: string\r\n ): Promise<boolean> {\r\n return await bcrypt.compare(candidatePassword, userPassword);\r\n }\r\n\r\n /**\r\n * Hashes a plain text password using bcrypt.\r\n *\r\n * @param {string} password - The password to be hashed.\r\n * @returns {Promise<string>} Returns the hashed password.\r\n */\r\n async hashPassword(password: string): Promise<string> {\r\n return await bcrypt.hash(password, 12);\r\n }\r\n\r\n /**\r\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\r\n *\r\n * **Note**: You can define it when calling arkos.init()\r\n * ```ts\r\n * arkos.init(app, {\r\n * authentication: {\r\n * passwordRegex: /your-desired-regex/\r\n * }\r\n * })\r\n * ```\r\n *\r\n * @param {string} password - The password to check.\r\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\r\n */\r\n isPasswordStrong(password: string): boolean {\r\n const initAuthConfigs = getInitConfigs()\r\n ?.authentication as InitConfigsAuthenticationOptions;\r\n\r\n const strongPasswordRegex =\r\n initAuthConfigs.passwordValidation?.regex ||\r\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\r\n return strongPasswordRegex.test(password);\r\n }\r\n\r\n /**\r\n * Checks if a user has changed their password after the JWT was issued.\r\n *\r\n * @param {User} user - The user object containing the passwordChangedAt field.\r\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\r\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\r\n */\r\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\r\n if (user.passwordChangedAt) {\r\n const convertedTimestamp = parseInt(\r\n String(user.passwordChangedAt.getTime() / 1000),\r\n 10\r\n );\r\n\r\n return JWTTimestamp < convertedTimestamp;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Verifies the authenticity of a JWT token.\r\n *\r\n * @param {string} token - The JWT token to verify.\r\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\r\n * @throws {Error} Throws an error if the token is invalid or expired.\r\n */\r\n async verifyJwtToken(\r\n token: string,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): Promise<AuthJwtPayload> {\r\n return new Promise((resolve, reject) => {\r\n jwt.verify(token, secret, (err, decoded) => {\r\n if (err) reject(err);\r\n else resolve(decoded as AuthJwtPayload);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Middleware function to handle access control based on user roles and permissions.\r\n *\r\n * @param {AuthConfigs} authConfigs - The configuration object for authentication and access control.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name that the action is being performed on (e.g., \"User\", \"Post\").\r\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\r\n */\r\n handleActionAccessControl(\r\n authConfigs: AuthConfigs,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n return catchAsync(\r\n async (\r\n req: ArkosRequest,\r\n res: ArkosResponse,\r\n next: ArkosNextFunction\r\n ) => {\r\n if (req.user) {\r\n const user = req.user as any;\r\n const prisma = getPrismaInstance();\r\n\r\n const permissions = await (prisma as any).authPermission.count({\r\n where: {\r\n resource: {\r\n equals: kebabCase(singular(modelName)),\r\n mode: \"insensitive\",\r\n },\r\n action,\r\n roleId: { in: user.roles.map((role: UserRole) => role.roleId) },\r\n },\r\n });\r\n\r\n if (!permissions) {\r\n return next(\r\n new AppError(\r\n \"You do not have permission to perfom this action\",\r\n 403\r\n )\r\n );\r\n }\r\n }\r\n\r\n next();\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * Processes the cookies or authoriation token and returns the user.\r\n * @param req\r\n * @returns {Promise<User | null>} - if authentication is turned off in initConfigs it returns null\r\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\r\n */\r\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return null;\r\n\r\n const prisma = getPrismaInstance();\r\n\r\n let token: string | undefined;\r\n\r\n if (\r\n req?.headers?.authorization &&\r\n req?.headers?.authorization.startsWith(\"Bearer\")\r\n ) {\r\n token = req?.headers?.authorization.split(\" \")[1];\r\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\r\n token = req?.cookies?.arkos_access_token;\r\n }\r\n\r\n if (!token)\r\n throw new AppError(\r\n \"You are not logged in! please log in to get access\",\r\n 401\r\n );\r\n\r\n let decoded: AuthJwtPayload | undefined;\r\n try {\r\n decoded = await this.verifyJwtToken(token);\r\n } catch (err) {\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n }\r\n\r\n if (!decoded?.id)\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n\r\n const user: any | null = await (prisma as any).user.findUnique({\r\n where: { id: String(decoded.id) },\r\n include: {\r\n roles: {\r\n include: {\r\n role: {\r\n include: {\r\n permissions: true,\r\n },\r\n },\r\n },\r\n },\r\n },\r\n });\r\n\r\n if (!user)\r\n throw new AppError(\r\n \"The user belonging to this token does no longer exists\",\r\n 401\r\n );\r\n\r\n if (\r\n this.userChangedPasswordAfter(user, decoded.iat!) &&\r\n !req.path.includes(\"logout\")\r\n )\r\n throw new AppError(\r\n \"User recently changed password! Please log in again.\",\r\n 401\r\n );\r\n\r\n return user;\r\n }\r\n\r\n /**\r\n * Middleware function to authenticate the user based on the JWT token.\r\n *\r\n * @param {ArkosRequest} req - The request object.\r\n * @param {ArkosResponse} res - The response object.\r\n * @param {ArkosNextFunction} next - The next middleware function to be called.\r\n * @returns {void}\r\n */\r\n authenticate = catchAsync(\r\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return next();\r\n\r\n req.user = (await this.getAuthenticatedUser(req)) as User;\r\n next();\r\n }\r\n );\r\n\r\n /**\r\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\r\n *\r\n * @param {AuthConfigs | undefined} authConfigs - The authentication configuration object.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name being affected by the action.\r\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\r\n */\r\n handleAuthenticationControl(\r\n authConfigs: AuthConfigs | undefined,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n const authenticationControl = authConfigs?.authenticationControl;\r\n\r\n if (authenticationControl && typeof authenticationControl === \"object\") {\r\n if (authenticationControl[action] === false) return callNext;\r\n else if (authenticationControl[action] === true) return this.authenticate;\r\n } else return this.authenticate;\r\n\r\n return this.authenticate;\r\n }\r\n}\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nconst authService = new AuthService();\r\n\r\nexport default authService;\r\n"]}
1
+ {"version":3,"file":"auth.service.js","sourceRoot":"","sources":["../../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"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","sourcesContent":["import jwt from \"jsonwebtoken\";\r\nimport bcrypt from \"bcryptjs\";\r\nimport { User, UserRole } from \"../../types\";\r\nimport catchAsync from \"../error-handler/utils/catch-async\";\r\nimport AppError from \"../error-handler/utils/app-error\";\r\nimport { callNext } from \"../base/base.middlewares\";\r\nimport { getInitConfigs } from \"../../server\";\r\nimport arkosEnv from \"../../utils/arkos-env\";\r\nimport { getPrismaInstance } from \"../../utils/helpers/prisma.helpers\";\r\nimport {\r\n ArkosRequest,\r\n ArkosResponse,\r\n ArkosNextFunction,\r\n ArkosRequestHandler,\r\n} from \"../../types\";\r\nimport {\r\n AuthConfigs,\r\n AuthJwtPayload,\r\n ControllerActions,\r\n} from \"../../types/auth\";\r\nimport { InitConfigsAuthenticationOptions } from \"../../types/init-configs\";\r\nimport { kebabCase } from \"../../utils/helpers/change-case.helpers\";\r\nimport { singular } from \"pluralize\";\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nclass AuthService {\r\n /**\r\n * Signs a JWT token for the user.\r\n *\r\n * @param {number | string} id - The unique identifier of the user to generate the token for.\r\n * @param {string | number} [expiresIn] - The expiration time for the token. Defaults to environment variable `JWT_EXPIRES_IN`.\r\n * @param {string} [secret] - The secret key used to sign the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {string} The signed JWT token.\r\n */\r\n signJwtToken(\r\n id: number | string,\r\n expiresIn: string | number = process.env.JWT_EXPIRES_IN ||\r\n arkosEnv.JWT_EXPIRES_IN,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): string {\r\n return jwt.sign({ id }, secret, {\r\n expiresIn: expiresIn as any,\r\n });\r\n }\r\n\r\n /**\r\n * Compares a candidate password with the stored user password to check if they match.\r\n *\r\n * @param {string} candidatePassword - The password provided by the user during login.\r\n * @param {string} userPassword - The password stored in the database.\r\n * @returns {Promise<boolean>} Returns true if the passwords match, otherwise false.\r\n */\r\n async isCorrectPassword(\r\n candidatePassword: string,\r\n userPassword: string\r\n ): Promise<boolean> {\r\n return await bcrypt.compare(candidatePassword, userPassword);\r\n }\r\n\r\n /**\r\n * Hashes a plain text password using bcrypt.\r\n *\r\n * @param {string} password - The password to be hashed.\r\n * @returns {Promise<string>} Returns the hashed password.\r\n */\r\n async hashPassword(password: string): Promise<string> {\r\n return await bcrypt.hash(password, 12);\r\n }\r\n\r\n /**\r\n * Checks if a password is strong, requiring uppercase, lowercase, and numeric characters as the default.\r\n *\r\n * **Note**: You can define it when calling arkos.init()\r\n * ```ts\r\n * arkos.init({\r\n * authentication: {\r\n * passwordValidation:{ regex: /your-desired-regex/, message: 'password must contain...'}\r\n * }\r\n * })\r\n * ```\r\n *\r\n * @param {string} password - The password to check.\r\n * @returns {boolean} Returns true if the password meets the strength criteria, otherwise false.\r\n */\r\n isPasswordStrong(password: string): boolean {\r\n const initAuthConfigs = getInitConfigs()\r\n ?.authentication as InitConfigsAuthenticationOptions;\r\n\r\n const strongPasswordRegex =\r\n initAuthConfigs.passwordValidation?.regex ||\r\n /^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$/;\r\n return strongPasswordRegex.test(password);\r\n }\r\n\r\n /**\r\n * Checks if a user has changed their password after the JWT was issued.\r\n *\r\n * @param {User} user - The user object containing the passwordChangedAt field.\r\n * @param {number} JWTTimestamp - The timestamp when the JWT was issued.\r\n * @returns {boolean} Returns true if the user changed their password after the JWT was issued, otherwise false.\r\n */\r\n userChangedPasswordAfter(user: User, JWTTimestamp: number): boolean {\r\n if (user.passwordChangedAt) {\r\n const convertedTimestamp = parseInt(\r\n String(user.passwordChangedAt.getTime() / 1000),\r\n 10\r\n );\r\n\r\n return JWTTimestamp < convertedTimestamp;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Verifies the authenticity of a JWT token.\r\n *\r\n * @param {string} token - The JWT token to verify.\r\n * @param {string} [secret] - The secret key used to verify the token. Defaults to environment variable `JWT_SECRET`.\r\n * @returns {Promise<AuthJwtPayload>} Returns the decoded JWT payload if the token is valid.\r\n * @throws {Error} Throws an error if the token is invalid or expired.\r\n */\r\n async verifyJwtToken(\r\n token: string,\r\n secret: string = process.env.JWT_SECRET || arkosEnv.JWT_SECRET\r\n ): Promise<AuthJwtPayload> {\r\n return new Promise((resolve, reject) => {\r\n jwt.verify(token, secret, (err, decoded) => {\r\n if (err) reject(err);\r\n else resolve(decoded as AuthJwtPayload);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Middleware function to handle access control based on user roles and permissions.\r\n *\r\n * @param {AuthConfigs} authConfigs - The configuration object for authentication and access control.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name that the action is being performed on (e.g., \"User\", \"Post\").\r\n * @returns {ArkosRequestHandler} The middleware function that checks if the user has permission to perform the action.\r\n */\r\n handleActionAccessControl(\r\n authConfigs: AuthConfigs,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n return catchAsync(\r\n async (\r\n req: ArkosRequest,\r\n res: ArkosResponse,\r\n next: ArkosNextFunction\r\n ) => {\r\n if (req.user) {\r\n const user = req.user as any;\r\n const prisma = getPrismaInstance();\r\n\r\n const permissions = await (prisma as any).authPermission.count({\r\n where: {\r\n resource: kebabCase(singular(modelName)),\r\n action,\r\n roleId: { in: user.roles.map((role: UserRole) => role.roleId) },\r\n },\r\n });\r\n\r\n if (!permissions) {\r\n return next(\r\n new AppError(\r\n \"You do not have permission to perfom this action\",\r\n 403\r\n )\r\n );\r\n }\r\n }\r\n\r\n next();\r\n }\r\n );\r\n }\r\n\r\n /**\r\n * Processes the cookies or authoriation token and returns the user.\r\n * @param req\r\n * @returns {Promise<User | null>} - if authentication is turned off in initConfigs it returns null\r\n * @throws {AppError} Throws an error if the token is invalid or the user is not logged in.\r\n */\r\n async getAuthenticatedUser(req: ArkosRequest): Promise<User | null> {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return null;\r\n\r\n const prisma = getPrismaInstance();\r\n\r\n let token: string | undefined;\r\n\r\n if (\r\n req?.headers?.authorization &&\r\n req?.headers?.authorization.startsWith(\"Bearer\")\r\n ) {\r\n token = req?.headers?.authorization.split(\" \")[1];\r\n } else if (req?.cookies?.arkos_access_token !== \"no-token\" && req.cookies) {\r\n token = req?.cookies?.arkos_access_token;\r\n }\r\n\r\n if (!token)\r\n throw new AppError(\r\n \"You are not logged in! please log in to get access\",\r\n 401\r\n );\r\n\r\n let decoded: AuthJwtPayload | undefined;\r\n try {\r\n decoded = await this.verifyJwtToken(token);\r\n } catch (err) {\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n }\r\n\r\n if (!decoded?.id)\r\n throw new AppError(\r\n \"Your auth token is invalid, please login again.\",\r\n 401\r\n );\r\n\r\n const user: any | null = await (prisma as any).user.findUnique({\r\n where: { id: String(decoded.id) },\r\n include: {\r\n roles: {\r\n include: {\r\n role: {\r\n include: {\r\n permissions: true,\r\n },\r\n },\r\n },\r\n },\r\n },\r\n });\r\n\r\n if (!user)\r\n throw new AppError(\r\n \"The user belonging to this token does no longer exists\",\r\n 401\r\n );\r\n\r\n if (\r\n this.userChangedPasswordAfter(user, decoded.iat!) &&\r\n !req.path.includes(\"logout\")\r\n )\r\n throw new AppError(\r\n \"User recently changed password! Please log in again.\",\r\n 401\r\n );\r\n\r\n return user;\r\n }\r\n\r\n /**\r\n * Middleware function to authenticate the user based on the JWT token.\r\n *\r\n * @param {ArkosRequest} req - The request object.\r\n * @param {ArkosResponse} res - The response object.\r\n * @param {ArkosNextFunction} next - The next middleware function to be called.\r\n * @returns {void}\r\n */\r\n authenticate = catchAsync(\r\n async (req: ArkosRequest, res: ArkosResponse, next: ArkosNextFunction) => {\r\n const initConfigs = getInitConfigs();\r\n if (initConfigs?.authentication === false) return next();\r\n\r\n req.user = (await this.getAuthenticatedUser(req)) as User;\r\n next();\r\n }\r\n );\r\n\r\n /**\r\n * Handles authentication control by checking the `authenticationControl` configuration in the `authConfigs`.\r\n *\r\n * @param {AuthConfigs | undefined} authConfigs - The authentication configuration object.\r\n * @param {ControllerActions} action - The action being performed (e.g., create, update, delete, view).\r\n * @param {string} modelName - The model name being affected by the action.\r\n * @returns {ArkosRequestHandler} The middleware function that checks if authentication is required.\r\n */\r\n handleAuthenticationControl(\r\n authConfigs: AuthConfigs | undefined,\r\n action: ControllerActions,\r\n modelName: string\r\n ): ArkosRequestHandler {\r\n const authenticationControl = authConfigs?.authenticationControl;\r\n\r\n if (authenticationControl && typeof authenticationControl === \"object\") {\r\n if (authenticationControl[action] === false) return callNext;\r\n else if (authenticationControl[action] === true) return this.authenticate;\r\n } else return this.authenticate;\r\n\r\n return this.authenticate;\r\n }\r\n}\r\n\r\n/**\r\n * Handles various authentication-related tasks such as JWT signing, password hashing, and verifying user credentials.\r\n */\r\nconst authService = new AuthService();\r\n\r\nexport default authService;\r\n"]}
@@ -1 +1 @@
1
- "use strict";import{getPrismaModelRelations as m}from"../../../utils/helpers/models.helpers";export function handleRelationFieldsInBody(n,p,t=[]){let a={...n};return p.list.forEach(e=>{if(!n[e.name])return;const s=[],c=[],l=[],o=[],h=[];n[e.name].forEach(i=>{if(!t.includes(i?.apiAction))if(i?.apiAction==="delete")h.push(i.id);else if(i?.apiAction==="disconnect")o.push({id:i.id});else if(i?.id)if(Object.keys(i).length===1)c.push(i);else{const{id:d,...r}=i;let u=m(e.type);const f=u?handleRelationFieldsInBody(r,u,t):i;l.push({where:{id:d},data:f})}else{let d=m(e.type);const r=d?handleRelationFieldsInBody(i,d,t):i;s.push(r)}}),a[e.name]={...s.length?{create:s}:{},...c.length?{connect:c}:{},...l.length?{update:l}:{},...o.length?{disconnect:o}:{},...h.length?{deleteMany:{id:{in:h}}}:{}}}),p.singular.forEach(e=>{if(!n[e.name]||t.includes(n[e.name]?.apiAction))return;let s=m(e.type);if(!n[e.name]?.id)a[e.name]={create:handleRelationFieldsInBody(n[e.name],s,t)};else if(Object.keys(n[e.name]).length===1)a[e.name]={connect:n[e.name]};else{const{id:c,...l}=n[e.name];a[e.name]={update:{where:{id:c},data:handleRelationFieldsInBody(l,s,t)}}}}),a}
1
+ "use strict";import{getPrismaModelRelations as m}from"../../../utils/helpers/models.helpers";export function handleRelationFieldsInBody(n,p,t=[]){let a={...n};return p.list.forEach(e=>{if(!n[e.name])return;const s=[],l=[],c=[],o=[],h=[];n[e.name].forEach(i=>{if(!t.includes(i?.apiAction))if(i?.apiAction==="delete")h.push(i.id);else if(i?.apiAction==="disconnect")o.push({id:i.id});else if(i?.id)if(Object.keys(i).length===1)l.push(i);else{const{id:d,...r}=i;let f=m(e.type);const u=f?handleRelationFieldsInBody(r,f,t):i;c.push({where:{id:d},data:u})}else{let d=m(e.type);const r=d?handleRelationFieldsInBody(i,d,t):i;s.push(r)}}),a[e.name]={...s.length?{create:s}:{},...l.length?{connect:l}:{},...c.length?{update:c}:{},...o.length?{disconnect:o}:{},...h.length?{deleteMany:{id:{in:h}}}:{}}}),p.singular.forEach(e=>{if(!n[e.name]||t.includes(n[e.name]?.apiAction))return;let s=m(e.type);if(!n[e.name]?.id)a[e.name]={create:handleRelationFieldsInBody(n[e.name],s,t)};else if(Object.keys(n[e.name]).length===1)a[e.name]={connect:n[e.name]};else{const{id:l,...c}=n[e.name];a[e.name]={update:{where:{id:l},data:handleRelationFieldsInBody(c,s,t)}}}}),a}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkos",
3
- "version": "0.25.0-beta.1",
3
+ "version": "1.0.0-beta",
4
4
  "description": "A super customizable TypeScript package for auto-generating API routes with built-in authentication, email service, error handling, file upload routes, image upload optimization, and seamless Prisma integration.",
5
5
  "main": "dist/cjs/exports/index.js",
6
6
  "module": "dist/es2020/exports/index.js",
@@ -82,7 +82,8 @@
82
82
  "test:watch": "vitest",
83
83
  "test:ui": "vitest --ui",
84
84
  "test:coverage": "vitest run --coverage",
85
- "test:create": "ts-node src/scripts/create-test.ts"
85
+ "test:create": "ts-node src/scripts/create-test.ts",
86
+ "publish:beta-as-latest": "npm publish --tag beta && npm dist-tag add $(npm pkg get version | tr -d '\"') latest"
86
87
  },
87
88
  "keywords": [
88
89
  "auto API routes",