@skillstew/common 1.0.0 → 1.0.2

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.
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApplicationError = exports.InfrastructureError = exports.DomainError = exports.AppError = void 0;
4
+ class AppError extends Error {
5
+ constructor(message, code, context) {
6
+ super(message);
7
+ this.message = message;
8
+ this.code = code;
9
+ this.context = context;
10
+ this.name = this.constructor.name;
11
+ Object.setPrototypeOf(this, new.target.prototype);
12
+ }
13
+ }
14
+ exports.AppError = AppError;
15
+ class DomainError extends AppError {
16
+ }
17
+ exports.DomainError = DomainError;
18
+ class InfrastructureError extends AppError {
19
+ }
20
+ exports.InfrastructureError = InfrastructureError;
21
+ class ApplicationError extends AppError {
22
+ }
23
+ exports.ApplicationError = ApplicationError;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TokenRoleMismatchError = exports.InvalidTokenRoleError = exports.InvalidTokenError = exports.AccessTokenVerifyError = exports.RefreshTokenVerifyError = exports.EmailVerificationJwtVerifyError = exports.JwtError = void 0;
4
+ const AppError_1 = require("./AppError");
5
+ const JwtErrorCodes_1 = require("./codes/JwtErrorCodes");
6
+ class JwtError extends AppError_1.InfrastructureError {
7
+ constructor(code) {
8
+ super(JwtErrorCodes_1.JwtErrorCodes[code], code);
9
+ }
10
+ toJSON() {
11
+ return { error: this.name, message: this.message, code: this.code };
12
+ }
13
+ }
14
+ exports.JwtError = JwtError;
15
+ class EmailVerificationJwtVerifyError extends JwtError {
16
+ constructor() {
17
+ super("EMAIL_VERIFICATION_JWT_VERIFY_ERROR");
18
+ }
19
+ }
20
+ exports.EmailVerificationJwtVerifyError = EmailVerificationJwtVerifyError;
21
+ class RefreshTokenVerifyError extends JwtError {
22
+ constructor() {
23
+ super("REFRESH_TOKEN_VERIFY_ERROR");
24
+ }
25
+ }
26
+ exports.RefreshTokenVerifyError = RefreshTokenVerifyError;
27
+ class AccessTokenVerifyError extends JwtError {
28
+ constructor() {
29
+ super("ACCESS_TOKEN_VERIFY_ERROR");
30
+ }
31
+ }
32
+ exports.AccessTokenVerifyError = AccessTokenVerifyError;
33
+ class InvalidTokenError extends JwtError {
34
+ constructor() {
35
+ super("INVALID_TOKEN_ERROR");
36
+ }
37
+ }
38
+ exports.InvalidTokenError = InvalidTokenError;
39
+ class InvalidTokenRoleError extends JwtError {
40
+ constructor() {
41
+ super("INVALID_TOKEN_ROLE_ERROR");
42
+ }
43
+ }
44
+ exports.InvalidTokenRoleError = InvalidTokenRoleError;
45
+ class TokenRoleMismatchError extends JwtError {
46
+ constructor() {
47
+ super("TOKEN_ROLE_MISMATCH_ERROR");
48
+ }
49
+ }
50
+ exports.TokenRoleMismatchError = TokenRoleMismatchError;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnauthenticatedError = void 0;
4
+ const AppError_1 = require("./AppError");
5
+ class UnauthenticatedError extends AppError_1.ApplicationError {
6
+ constructor() {
7
+ super("Unauthenticated", "USER_UNAUTHENTICATED");
8
+ }
9
+ toJSON() {
10
+ return { name: this.name, message: this.message };
11
+ }
12
+ }
13
+ exports.UnauthenticatedError = UnauthenticatedError;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JwtErrorCodes = void 0;
4
+ var JwtErrorCodes;
5
+ (function (JwtErrorCodes) {
6
+ JwtErrorCodes["EMAIL_VERIFICATION_JWT_VERIFY_ERROR"] = "Invalid email verification jwt";
7
+ JwtErrorCodes["REFRESH_TOKEN_VERIFY_ERROR"] = "Invalid refresh token";
8
+ JwtErrorCodes["ACCESS_TOKEN_VERIFY_ERROR"] = "Invalid access token";
9
+ JwtErrorCodes["INVALID_TOKEN_ERROR"] = "Invalid token";
10
+ JwtErrorCodes["INVALID_TOKEN_ROLE_ERROR"] = "Invalid role identifier";
11
+ JwtErrorCodes["TOKEN_ROLE_MISMATCH_ERROR"] = "Role mismatch between payload and header";
12
+ })(JwtErrorCodes || (exports.JwtErrorCodes = JwtErrorCodes = {}));
package/build/index.js ADDED
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./errors/AppError"), exports);
18
+ __exportStar(require("./errors/JwtErrors"), exports);
19
+ __exportStar(require("./errors/UnauthenticatedError"), exports);
20
+ __exportStar(require("./errors/codes/JwtErrorCodes"), exports);
21
+ __exportStar(require("./jwt-utils/JwtHelper"), exports);
22
+ __exportStar(require("./middlewares/authMiddleware"), exports);
23
+ __exportStar(require("./types/UserRoles"), exports);
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JwtHelper = void 0;
7
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
8
+ const JwtErrors_1 = require("../errors/JwtErrors");
9
+ function isUserRole(role) {
10
+ return ["ADMIN", "EXPERT", "USER"].includes(role);
11
+ }
12
+ class JwtHelper {
13
+ constructor({ userAccessTokenSecret, expertAccessTokenSecret, adminAccessTokenSecret, }) {
14
+ this.verifyAccessToken = (jwtToken) => {
15
+ const decoded = jsonwebtoken_1.default.decode(jwtToken, { complete: true });
16
+ if (!decoded || !decoded.header) {
17
+ throw new JwtErrors_1.InvalidTokenError();
18
+ }
19
+ const header = decoded.header;
20
+ const role = header.kid;
21
+ if (!role || !isUserRole(role)) {
22
+ throw new JwtErrors_1.InvalidTokenRoleError();
23
+ }
24
+ let payload;
25
+ try {
26
+ payload = jsonwebtoken_1.default.verify(jwtToken, this._AccessSecrets[role]);
27
+ }
28
+ catch (err) {
29
+ throw new JwtErrors_1.AccessTokenVerifyError();
30
+ }
31
+ if (!(role === payload.role)) {
32
+ throw new JwtErrors_1.TokenRoleMismatchError();
33
+ }
34
+ return payload;
35
+ };
36
+ this._AccessSecrets = {
37
+ USER: userAccessTokenSecret,
38
+ ADMIN: adminAccessTokenSecret,
39
+ EXPERT: expertAccessTokenSecret,
40
+ };
41
+ }
42
+ }
43
+ exports.JwtHelper = JwtHelper;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthMiddleware = void 0;
4
+ const UnauthenticatedError_1 = require("../errors/UnauthenticatedError");
5
+ const JwtHelper_1 = require("../jwt-utils/JwtHelper");
6
+ class AuthMiddleware {
7
+ constructor(userAccessTokenSecret, expertAccessTokenSecret, adminAccessTokenSecret) {
8
+ this.verify = (req, _res, next) => {
9
+ var _a;
10
+ try {
11
+ const token = (_a = req.headers["authorization"]) === null || _a === void 0 ? void 0 : _a.split(" ")[1];
12
+ if (!token) {
13
+ throw new UnauthenticatedError_1.UnauthenticatedError();
14
+ }
15
+ const payload = this._jwtHelper.verifyAccessToken(token);
16
+ req.user = Object.assign(Object.assign({ id: payload.userId }, (payload.role === "ADMIN"
17
+ ? { userame: payload.username }
18
+ : { email: payload.email })), { role: payload.role });
19
+ next();
20
+ }
21
+ catch (err) {
22
+ next(err);
23
+ }
24
+ };
25
+ this._jwtHelper = new JwtHelper_1.JwtHelper({
26
+ userAccessTokenSecret,
27
+ expertAccessTokenSecret,
28
+ adminAccessTokenSecret,
29
+ });
30
+ }
31
+ }
32
+ exports.AuthMiddleware = AuthMiddleware;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.USER_ROLES = void 0;
4
+ exports.USER_ROLES = ["USER", "EXPERT", "ADMIN"];
package/package.json CHANGED
@@ -1,10 +1,22 @@
1
1
  {
2
2
  "name": "@skillstew/common",
3
- "version": "1.0.0",
4
- "main": "index.js",
5
- "scripts": {},
3
+ "version": "1.0.2",
4
+ "main": "./build/index.js",
5
+ "types": "./build/index.d.ts",
6
+ "scripts": {
7
+ "del": "del ./build/*",
8
+ "build": "npm run del && tsc",
9
+ "pub": "git add . && git commit -m \"Updates to common\" && npm version patch && npm run build && npm publish"
10
+ },
6
11
  "keywords": [],
7
12
  "author": "",
8
13
  "license": "ISC",
9
- "description": ""
14
+ "description": "",
15
+ "dependencies": {
16
+ "@types/express": "^5.0.3",
17
+ "@types/jsonwebtoken": "^9.0.10",
18
+ "del-cli": "^6.0.0",
19
+ "express": "^5.1.0",
20
+ "jsonwebtoken": "^9.0.2"
21
+ }
10
22
  }
@@ -0,0 +1,20 @@
1
+ export abstract class AppError extends Error {
2
+ public readonly name: string;
3
+ constructor(
4
+ public readonly message: string,
5
+ public readonly code: string,
6
+ public readonly context?: Record<string, unknown>,
7
+ ) {
8
+ super(message);
9
+ this.name = this.constructor.name;
10
+ Object.setPrototypeOf(this, new.target.prototype);
11
+ }
12
+
13
+ abstract toJSON(): object;
14
+ }
15
+
16
+ export abstract class DomainError extends AppError {}
17
+
18
+ export abstract class InfrastructureError extends AppError {}
19
+
20
+ export abstract class ApplicationError extends AppError {}
@@ -0,0 +1,47 @@
1
+ import { InfrastructureError } from "./AppError";
2
+ import { JwtErrorCodes } from "./codes/JwtErrorCodes";
3
+
4
+ export class JwtError extends InfrastructureError {
5
+ constructor(code: keyof typeof JwtErrorCodes) {
6
+ super(JwtErrorCodes[code], code);
7
+ }
8
+ toJSON(): object {
9
+ return { error: this.name, message: this.message, code: this.code };
10
+ }
11
+ }
12
+
13
+ export class EmailVerificationJwtVerifyError extends JwtError {
14
+ constructor() {
15
+ super("EMAIL_VERIFICATION_JWT_VERIFY_ERROR");
16
+ }
17
+ }
18
+
19
+ export class RefreshTokenVerifyError extends JwtError {
20
+ constructor() {
21
+ super("REFRESH_TOKEN_VERIFY_ERROR");
22
+ }
23
+ }
24
+
25
+ export class AccessTokenVerifyError extends JwtError {
26
+ constructor() {
27
+ super("ACCESS_TOKEN_VERIFY_ERROR");
28
+ }
29
+ }
30
+
31
+ export class InvalidTokenError extends JwtError {
32
+ constructor() {
33
+ super("INVALID_TOKEN_ERROR");
34
+ }
35
+ }
36
+
37
+ export class InvalidTokenRoleError extends JwtError {
38
+ constructor() {
39
+ super("INVALID_TOKEN_ROLE_ERROR");
40
+ }
41
+ }
42
+
43
+ export class TokenRoleMismatchError extends JwtError {
44
+ constructor() {
45
+ super("TOKEN_ROLE_MISMATCH_ERROR");
46
+ }
47
+ }
@@ -0,0 +1,10 @@
1
+ import { ApplicationError } from "./AppError";
2
+
3
+ export class UnauthenticatedError extends ApplicationError {
4
+ constructor() {
5
+ super("Unauthenticated", "USER_UNAUTHENTICATED");
6
+ }
7
+ toJSON(): object {
8
+ return { name: this.name, message: this.message };
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ export enum JwtErrorCodes {
2
+ EMAIL_VERIFICATION_JWT_VERIFY_ERROR = "Invalid email verification jwt",
3
+ REFRESH_TOKEN_VERIFY_ERROR = "Invalid refresh token",
4
+ ACCESS_TOKEN_VERIFY_ERROR = "Invalid access token",
5
+ INVALID_TOKEN_ERROR = "Invalid token",
6
+ INVALID_TOKEN_ROLE_ERROR = "Invalid role identifier",
7
+ TOKEN_ROLE_MISMATCH_ERROR = "Role mismatch between payload and header",
8
+ }
package/src/index.ts CHANGED
@@ -0,0 +1,9 @@
1
+ export * from "./errors/AppError";
2
+ export * from "./errors/JwtErrors";
3
+ export * from "./errors/UnauthenticatedError";
4
+ export * from "./errors/codes/JwtErrorCodes";
5
+
6
+ export * from "./jwt-utils/JwtHelper";
7
+ export * from "./middlewares/authMiddleware";
8
+
9
+ export * from "./types/UserRoles";
@@ -0,0 +1,74 @@
1
+ import jwt, { JwtHeader } from "jsonwebtoken";
2
+ import { UserRoles } from "../types/UserRoles";
3
+ import {
4
+ AccessTokenVerifyError,
5
+ InvalidTokenError,
6
+ InvalidTokenRoleError,
7
+ TokenRoleMismatchError,
8
+ } from "../errors/JwtErrors";
9
+
10
+ function isUserRole(role: string): role is UserRoles {
11
+ return ["ADMIN", "EXPERT", "USER"].includes(role);
12
+ }
13
+
14
+ export type tokenBody =
15
+ | {
16
+ userId: string;
17
+ email: string;
18
+ role: Exclude<UserRoles, "ADMIN">;
19
+ }
20
+ | {
21
+ userId: string;
22
+ username: string;
23
+ role: "ADMIN";
24
+ };
25
+
26
+ export type JWTPayload = tokenBody & {
27
+ iat: number;
28
+ exp: number;
29
+ };
30
+
31
+ export class JwtHelper {
32
+ private _AccessSecrets: Record<UserRoles, string>;
33
+ constructor({
34
+ userAccessTokenSecret,
35
+ expertAccessTokenSecret,
36
+ adminAccessTokenSecret,
37
+ }: {
38
+ userAccessTokenSecret: string;
39
+ expertAccessTokenSecret: string;
40
+ adminAccessTokenSecret: string;
41
+ }) {
42
+ this._AccessSecrets = {
43
+ USER: userAccessTokenSecret,
44
+ ADMIN: adminAccessTokenSecret,
45
+ EXPERT: expertAccessTokenSecret,
46
+ };
47
+ }
48
+
49
+ verifyAccessToken = (jwtToken: string) => {
50
+ const decoded = jwt.decode(jwtToken, { complete: true }) as any;
51
+ if (!decoded || !decoded.header) {
52
+ throw new InvalidTokenError();
53
+ }
54
+
55
+ const header = decoded.header as JwtHeader & { kid?: string };
56
+ const role = header.kid as UserRoles | undefined;
57
+
58
+ if (!role || !isUserRole(role)) {
59
+ throw new InvalidTokenRoleError();
60
+ }
61
+ let payload: JWTPayload;
62
+ try {
63
+ payload = <JWTPayload>jwt.verify(jwtToken, this._AccessSecrets[role]);
64
+ } catch (err) {
65
+ throw new AccessTokenVerifyError();
66
+ }
67
+
68
+ if (!(role === payload.role)) {
69
+ throw new TokenRoleMismatchError();
70
+ }
71
+
72
+ return payload;
73
+ };
74
+ }
@@ -0,0 +1,39 @@
1
+ import { RequestHandler } from "express";
2
+ import { UnauthenticatedError } from "../errors/UnauthenticatedError";
3
+ import { JwtHelper } from "../jwt-utils/JwtHelper";
4
+
5
+ export class AuthMiddleware {
6
+ private _jwtHelper: JwtHelper;
7
+
8
+ constructor(
9
+ userAccessTokenSecret: string,
10
+ expertAccessTokenSecret: string,
11
+ adminAccessTokenSecret: string,
12
+ ) {
13
+ this._jwtHelper = new JwtHelper({
14
+ userAccessTokenSecret,
15
+ expertAccessTokenSecret,
16
+ adminAccessTokenSecret,
17
+ });
18
+ }
19
+
20
+ verify: RequestHandler = (req, _res, next) => {
21
+ try {
22
+ const token = req.headers["authorization"]?.split(" ")[1];
23
+ if (!token) {
24
+ throw new UnauthenticatedError();
25
+ }
26
+ const payload = this._jwtHelper.verifyAccessToken(token);
27
+ req.user = {
28
+ id: payload.userId,
29
+ ...(payload.role === "ADMIN"
30
+ ? { userame: payload.username }
31
+ : { email: payload.email }),
32
+ role: payload.role,
33
+ };
34
+ next();
35
+ } catch (err) {
36
+ next(err);
37
+ }
38
+ };
39
+ }
@@ -0,0 +1,2 @@
1
+ export const USER_ROLES = ["USER", "EXPERT", "ADMIN"] as const;
2
+ export type UserRoles = (typeof USER_ROLES)[number];
@@ -0,0 +1,16 @@
1
+ import { RequestUser } from "../../3-presentation/types/RequestType";
2
+ import { UserRoles } from "../../0-domain/entities/UserRoles";
3
+
4
+ export interface RequestUser {
5
+ id: string | number;
6
+ email: string;
7
+ role: UserRoles;
8
+ }
9
+
10
+ declare global {
11
+ namespace Express {
12
+ interface Request {
13
+ user: RequestUser;
14
+ }
15
+ }
16
+ }
package/tsconfig.json CHANGED
@@ -11,7 +11,7 @@
11
11
  // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12
12
 
13
13
  /* Language and Environment */
14
- "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
14
+ "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
15
15
  // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16
16
  // "jsx": "preserve", /* Specify what JSX code is generated. */
17
17
  // "libReplacement": true, /* Enable lib replacement. */
@@ -26,13 +26,15 @@
26
26
  // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
27
27
 
28
28
  /* Modules */
29
- "module": "commonjs", /* Specify what module code is generated. */
29
+ "module": "commonjs" /* Specify what module code is generated. */,
30
30
  // "rootDir": "./", /* Specify the root folder within your source files. */
31
31
  // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
32
32
  // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33
33
  // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
34
34
  // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35
- // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35
+ "typeRoots": [
36
+ "./src/types"
37
+ ] /* Specify multiple folders that act like './node_modules/@types'. */,
36
38
  // "types": [], /* Specify type package names to be included without being referenced in a source file. */
37
39
  // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
38
40
  // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
@@ -59,7 +61,7 @@
59
61
  // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
60
62
  // "noEmit": true, /* Disable emitting files from a compilation. */
61
63
  // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
62
- // "outDir": "./", /* Specify an output folder for all emitted files. */
64
+ "outDir": "./build" /* Specify an output folder for all emitted files. */,
63
65
  // "removeComments": true, /* Disable emitting comments. */
64
66
  // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
65
67
  // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
@@ -80,12 +82,12 @@
80
82
  // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
81
83
  // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
82
84
  // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
83
- "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
85
+ "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
84
86
  // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
85
- "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
87
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
86
88
 
87
89
  /* Type Checking */
88
- "strict": true, /* Enable all strict type-checking options. */
90
+ "strict": true /* Enable all strict type-checking options. */,
89
91
  // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
90
92
  // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
91
93
  // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
@@ -108,6 +110,6 @@
108
110
 
109
111
  /* Completeness */
110
112
  // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
111
- "skipLibCheck": true /* Skip type checking all .d.ts files. */
113
+ "skipLibCheck": true /* Skip type checking all .d.ts files. */
112
114
  }
113
115
  }