@epztickets/common 1.48.0 → 1.48.1

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,9 @@
1
+ import { CustomError } from "./custom-error";
2
+ export declare class ForbiddenError extends CustomError {
3
+ message: string;
4
+ statusCode: number;
5
+ constructor(message?: string);
6
+ serializeErrors(): {
7
+ message: string;
8
+ }[];
9
+ }
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ForbiddenError = void 0;
4
+ const custom_error_1 = require("./custom-error");
5
+ // 403 — the request is authenticated but the caller is not allowed to
6
+ // perform this action. Use this (not NotAuthorizedError) for role-based
7
+ // gates (requireOwner, requireAdmin) and per-resource ownership checks
8
+ // (e.g. "this room belongs to a different user").
9
+ //
10
+ // Why a separate error class from NotAuthorizedError (401):
11
+ //
12
+ // 401 means "we couldn't identify you" — your JWT is missing, expired, or
13
+ // malformed. The reasonable client response is to drop the session and ask
14
+ // the user to sign in again, which is what the frontend's axios
15
+ // interceptor does.
16
+ //
17
+ // 403 means "we know who you are but this isn't yours / your role can't
18
+ // do this". Forcing a re-signin would be hostile (the user's session is
19
+ // fine) and pointless (signing in again won't grant the missing role).
20
+ // The client should surface a normal error message instead and leave the
21
+ // session intact.
22
+ //
23
+ // HTTP RFC 9110 §15.5.4 ("the request was valid, but the server is
24
+ // refusing action; re-authenticating will make no difference") matches
25
+ // our intent here.
26
+ class ForbiddenError extends custom_error_1.CustomError {
27
+ constructor(message = "Forbidden") {
28
+ super(message);
29
+ this.message = message;
30
+ this.statusCode = 403;
31
+ Object.setPrototypeOf(this, ForbiddenError.prototype);
32
+ }
33
+ serializeErrors() {
34
+ return [{ message: this.message }];
35
+ }
36
+ }
37
+ exports.ForbiddenError = ForbiddenError;
package/build/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from "./errors/bad-request-error";
2
2
  export * from "./errors/custom-error";
3
3
  export * from "./errors/database-connection-error";
4
4
  export * from "./errors/not-authorized-error";
5
+ export * from "./errors/forbidden-error";
5
6
  export * from "./errors/not-found-error";
6
7
  export * from "./errors/request-validation-error";
7
8
  export * from "./middlewares/current-user";
package/build/index.js CHANGED
@@ -19,6 +19,7 @@ __exportStar(require("./errors/bad-request-error"), exports);
19
19
  __exportStar(require("./errors/custom-error"), exports);
20
20
  __exportStar(require("./errors/database-connection-error"), exports);
21
21
  __exportStar(require("./errors/not-authorized-error"), exports);
22
+ __exportStar(require("./errors/forbidden-error"), exports);
22
23
  __exportStar(require("./errors/not-found-error"), exports);
23
24
  __exportStar(require("./errors/request-validation-error"), exports);
24
25
  // middlewares
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requireAdmin = void 0;
4
- const not_authorized_error_1 = require("../errors/not-authorized-error");
4
+ const forbidden_error_1 = require("../errors/forbidden-error");
5
+ // Role gate: caller must be an admin. See require-owner.ts for the
6
+ // rationale on ForbiddenError (403) vs NotAuthorizedError (401).
5
7
  const requireAdmin = (req, res, next) => {
6
8
  var _a;
7
9
  if (((_a = req.currentUser) === null || _a === void 0 ? void 0 : _a.role) !== "admin") {
8
- throw new not_authorized_error_1.NotAuthorizedError();
10
+ throw new forbidden_error_1.ForbiddenError("Admin access required");
9
11
  }
10
12
  next();
11
13
  };
@@ -1,11 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.requireOwner = void 0;
4
- const not_authorized_error_1 = require("../errors/not-authorized-error");
4
+ const forbidden_error_1 = require("../errors/forbidden-error");
5
+ // Role gate: caller must be an owner or admin.
6
+ //
7
+ // Throws ForbiddenError (403) — NOT NotAuthorizedError (401) — when the
8
+ // user is signed in but doesn't hold the required role. The distinction
9
+ // matters end-to-end: the frontend axios interceptor force-signs-out on
10
+ // 401 (assuming the JWT is bad), but on 403 it lets the caller render
11
+ // a "you don't have access" message without destroying the session.
12
+ //
13
+ // If req.currentUser is missing entirely (no JWT, expired JWT, etc.),
14
+ // that's an authentication failure and should be caught upstream by
15
+ // requireAuth, which still throws NotAuthorizedError (401). This
16
+ // middleware assumes currentUser is populated.
5
17
  const requireOwner = (req, res, next) => {
6
18
  var _a, _b;
7
19
  if (((_a = req.currentUser) === null || _a === void 0 ? void 0 : _a.role) !== "owner" && ((_b = req.currentUser) === null || _b === void 0 ? void 0 : _b.role) !== "admin") {
8
- throw new not_authorized_error_1.NotAuthorizedError();
20
+ throw new forbidden_error_1.ForbiddenError("Owner access required");
9
21
  }
10
22
  next();
11
23
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epztickets/common",
3
- "version": "1.48.0",
3
+ "version": "1.48.1",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [