@innvoid/getmarket-sdk 0.1.3 → 0.1.4

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,181 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/auth/index.ts
31
+ var auth_exports = {};
32
+ __export(auth_exports, {
33
+ createAuthMiddleware: () => createAuthMiddleware,
34
+ readRs256PublicKey: () => readRs256PublicKey,
35
+ verifyBackendJwtRS256: () => verifyBackendJwtRS256
36
+ });
37
+ module.exports = __toCommonJS(auth_exports);
38
+
39
+ // src/auth/jwt.ts
40
+ var import_fs = __toESM(require("fs"), 1);
41
+ var import_jsonwebtoken = __toESM(require("jsonwebtoken"), 1);
42
+ function readFileIfExists(path) {
43
+ if (!path) return null;
44
+ try {
45
+ const v = import_fs.default.readFileSync(path, "utf8").trim();
46
+ return v.length ? v : null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ function readRs256PublicKey() {
52
+ const fromFile = readFileIfExists(process.env.JWT_PUBLIC_KEY_PATH);
53
+ if (fromFile) return fromFile;
54
+ const fromEnv = String(process.env.AUTH_JWT_PUBLIC_KEY || process.env.AUTH_RSA_PUBLIC_KEY || "").replace(/\\n/g, "\n").trim();
55
+ if (fromEnv) return fromEnv;
56
+ throw new Error("Missing RS256 public key (JWT_PUBLIC_KEY_PATH / AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY)");
57
+ }
58
+ function verifyBackendJwtRS256(raw) {
59
+ const publicKey = readRs256PublicKey();
60
+ const audience = process.env.JWT_AUDIENCE || process.env.AUTH_JWT_AUDIENCE || "getmarket.api";
61
+ const issuer = process.env.JWT_ISSUER || process.env.AUTH_JWT_ISSUER || "getmarket-auth";
62
+ return import_jsonwebtoken.default.verify(raw, publicKey, {
63
+ algorithms: ["RS256"],
64
+ audience,
65
+ issuer
66
+ });
67
+ }
68
+
69
+ // src/auth/middleware.ts
70
+ function getBearerToken(req) {
71
+ const auth = String(req.headers?.authorization || "");
72
+ if (!auth.startsWith("Bearer ")) return null;
73
+ const token = auth.slice(7).trim();
74
+ return token.length ? token : null;
75
+ }
76
+ function normalizeUid(v) {
77
+ const s = String(v ?? "").trim();
78
+ return s.length ? s : null;
79
+ }
80
+ function createAuthMiddleware(opts) {
81
+ const {
82
+ subject,
83
+ allowFirebaseIdToken = false,
84
+ requireSubject = true,
85
+ hydrate
86
+ } = opts;
87
+ return async (req, res, next) => {
88
+ const token = getBearerToken(req);
89
+ if (!token) {
90
+ return res.status(401).json({
91
+ ok: false,
92
+ code: "AUTH_MISSING_TOKEN",
93
+ message: "Missing Authorization Bearer token"
94
+ });
95
+ }
96
+ const headerCtx = req.context || {};
97
+ const company_uid = normalizeUid(headerCtx.company_uid);
98
+ const branch_uid = normalizeUid(headerCtx.branch_uid);
99
+ try {
100
+ const decoded = verifyBackendJwtRS256(token);
101
+ const baseCtx = {
102
+ tokenType: "backend",
103
+ subject,
104
+ company_uid: company_uid ?? void 0,
105
+ branch_uid: branch_uid ?? void 0,
106
+ roles: Array.isArray(decoded?.roles) ? decoded.roles : [],
107
+ permissions: Array.isArray(decoded?.permissions) ? decoded.permissions : [],
108
+ denied_permissions: Array.isArray(decoded?.denied_permissions) ? decoded.denied_permissions : [],
109
+ session: {
110
+ jti: decoded?.jti,
111
+ device_id: decoded?.device_id,
112
+ expires_at: decoded?.exp
113
+ }
114
+ };
115
+ const hydrated = await hydrate({ decoded, req, subject, company_uid, branch_uid });
116
+ Object.assign(baseCtx, hydrated);
117
+ if (requireSubject) {
118
+ if (subject === "employee" && !baseCtx.employee) {
119
+ return res.status(401).json({
120
+ ok: false,
121
+ code: "AUTH_EMPLOYEE_NOT_FOUND",
122
+ message: "Employee not resolved by hydrator"
123
+ });
124
+ }
125
+ if (subject === "customer" && !baseCtx.customer) {
126
+ return res.status(401).json({
127
+ ok: false,
128
+ code: "AUTH_CUSTOMER_NOT_FOUND",
129
+ message: "Customer not resolved by hydrator"
130
+ });
131
+ }
132
+ }
133
+ req.auth = baseCtx;
134
+ return next();
135
+ } catch (errJwt) {
136
+ if (!allowFirebaseIdToken) {
137
+ return res.status(401).json({
138
+ ok: false,
139
+ code: "AUTH_INVALID_TOKEN",
140
+ message: "Invalid or expired token"
141
+ });
142
+ }
143
+ try {
144
+ const { default: admin } = await import("firebase-admin");
145
+ const firebaseDecoded = await admin.auth().verifyIdToken(token);
146
+ if (firebaseDecoded.email && firebaseDecoded.email_verified === false) {
147
+ return res.status(401).json({
148
+ ok: false,
149
+ code: "AUTH_EMAIL_NOT_VERIFIED",
150
+ message: "Email not verified"
151
+ });
152
+ }
153
+ req.auth = {
154
+ tokenType: "backend",
155
+ subject,
156
+ firebase: firebaseDecoded,
157
+ company_uid: company_uid ?? void 0,
158
+ branch_uid: branch_uid ?? void 0,
159
+ companies: [],
160
+ roles: [],
161
+ permissions: [],
162
+ denied_permissions: []
163
+ };
164
+ return next();
165
+ } catch {
166
+ return res.status(401).json({
167
+ ok: false,
168
+ code: "AUTH_INVALID_TOKEN",
169
+ message: "Invalid or expired token"
170
+ });
171
+ }
172
+ }
173
+ };
174
+ }
175
+ // Annotate the CommonJS export names for ESM import in node:
176
+ 0 && (module.exports = {
177
+ createAuthMiddleware,
178
+ readRs256PublicKey,
179
+ verifyBackendJwtRS256
180
+ });
181
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/auth/index.ts","../../src/auth/jwt.ts","../../src/auth/middleware.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./jwt\";\nexport * from \"./middleware\";\n","import fs from \"fs\";\nimport jwt, {JwtPayload} from \"jsonwebtoken\";\n\nfunction readFileIfExists(path?: string): string | null {\n if (!path) return null;\n try {\n const v = fs.readFileSync(path, \"utf8\").trim();\n return v.length ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * ✅ Keys viven en getmarket-stack:\n * - JWT_PUBLIC_KEY_PATH=/run/secrets/jwtRS256.key.pub (recomendado)\n * - fallback env AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY\n */\nexport function readRs256PublicKey(): string {\n const fromFile = readFileIfExists(process.env.JWT_PUBLIC_KEY_PATH);\n if (fromFile) return fromFile;\n\n const fromEnv = String(process.env.AUTH_JWT_PUBLIC_KEY || process.env.AUTH_RSA_PUBLIC_KEY || \"\")\n .replace(/\\\\n/g, \"\\n\")\n .trim();\n\n if (fromEnv) return fromEnv;\n\n throw new Error(\"Missing RS256 public key (JWT_PUBLIC_KEY_PATH / AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY)\");\n}\n\nexport function verifyBackendJwtRS256(raw: string): JwtPayload {\n const publicKey = readRs256PublicKey();\n\n const audience = process.env.JWT_AUDIENCE || process.env.AUTH_JWT_AUDIENCE || \"getmarket.api\";\n const issuer = process.env.JWT_ISSUER || process.env.AUTH_JWT_ISSUER || \"getmarket-auth\";\n\n // ✅ SOLO RS256\n return jwt.verify(raw, publicKey, {\n algorithms: [\"RS256\"],\n audience,\n issuer,\n }) as JwtPayload;\n}\n","import type {NextFunction, Response} from \"express\";\nimport {verifyBackendJwtRS256} from \"./jwt\";\nimport type {AuthContext, AuthMiddlewareOptions} from \"./types\";\n\nfunction getBearerToken(req: any): string | null {\n const auth = String(req.headers?.authorization || \"\");\n if (!auth.startsWith(\"Bearer \")) return null;\n const token = auth.slice(7).trim();\n return token.length ? token : null;\n}\n\nfunction normalizeUid(v: any): string | null {\n const s = String(v ?? \"\").trim();\n return s.length ? s : null;\n}\n\n/**\n * ✅ Middleware estándar:\n * - Solo Authorization: Bearer\n * - Solo RS256\n * - Cero legacy\n * - Hidrata vía hook (OBLIGATORIO) para que cada micro no replique lógica\n */\nexport function createAuthMiddleware(opts: AuthMiddlewareOptions) {\n const {\n subject,\n allowFirebaseIdToken = false,\n requireSubject = true,\n hydrate,\n } = opts;\n\n return async (req: any, res: Response, next: NextFunction) => {\n const token = getBearerToken(req);\n if (!token) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_MISSING_TOKEN\",\n message: \"Missing Authorization Bearer token\",\n });\n }\n\n // Contexto desde parseHeaders (SDK) -> req.context\n const headerCtx = (req as any).context || {};\n const company_uid = normalizeUid(headerCtx.company_uid);\n const branch_uid = normalizeUid(headerCtx.branch_uid);\n\n // 1) RS256 backend JWT\n try {\n const decoded: any = verifyBackendJwtRS256(token);\n\n const baseCtx: AuthContext = {\n tokenType: \"backend\",\n subject,\n company_uid: company_uid ?? undefined,\n branch_uid: branch_uid ?? undefined,\n roles: Array.isArray(decoded?.roles) ? decoded.roles : [],\n permissions: Array.isArray(decoded?.permissions) ? decoded.permissions : [],\n denied_permissions: Array.isArray(decoded?.denied_permissions) ? decoded.denied_permissions : [],\n session: {\n jti: decoded?.jti,\n device_id: decoded?.device_id,\n expires_at: decoded?.exp,\n },\n };\n\n // ✅ hydrate obligatorio (cero legacy)\n const hydrated = await hydrate({decoded, req, subject, company_uid, branch_uid});\n Object.assign(baseCtx, hydrated);\n\n if (requireSubject) {\n if (subject === \"employee\" && !baseCtx.employee) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_EMPLOYEE_NOT_FOUND\",\n message: \"Employee not resolved by hydrator\",\n });\n }\n if (subject === \"customer\" && !baseCtx.customer) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_CUSTOMER_NOT_FOUND\",\n message: \"Customer not resolved by hydrator\",\n });\n }\n }\n\n req.auth = baseCtx;\n return next();\n } catch (errJwt) {\n // 2) Firebase opcional (si está habilitado explícitamente)\n if (!allowFirebaseIdToken) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_INVALID_TOKEN\",\n message: \"Invalid or expired token\",\n });\n }\n\n try {\n // Import dinámico (pero firebase-admin está en deps del SDK)\n const {default: admin} = await import(\"firebase-admin\");\n const firebaseDecoded = await admin.auth().verifyIdToken(token);\n\n if (firebaseDecoded.email && firebaseDecoded.email_verified === false) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_EMAIL_NOT_VERIFIED\",\n message: \"Email not verified\",\n });\n }\n\n req.auth = {\n tokenType: \"backend\",\n subject,\n firebase: firebaseDecoded,\n company_uid: company_uid ?? undefined,\n branch_uid: branch_uid ?? undefined,\n companies: [],\n roles: [],\n permissions: [],\n denied_permissions: [],\n };\n\n return next();\n } catch {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_INVALID_TOKEN\",\n message: \"Invalid or expired token\",\n });\n }\n }\n };\n}\n"],"mappings":"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","names":["fs","jwt"]}
@@ -0,0 +1,3 @@
1
+ export { A as AuthContext, a as AuthMiddlewareOptions, b as AuthSession, c as AuthSubject, H as HydrateInput, d as HydrateResult, e as Hydrator, T as TokenType, f as createAuthMiddleware, r as readRs256PublicKey, v as verifyBackendJwtRS256 } from '../index-WbfzvmOt.cjs';
2
+ import 'express';
3
+ import 'jsonwebtoken';
@@ -0,0 +1,3 @@
1
+ export { A as AuthContext, a as AuthMiddlewareOptions, b as AuthSession, c as AuthSubject, H as HydrateInput, d as HydrateResult, e as Hydrator, T as TokenType, f as createAuthMiddleware, r as readRs256PublicKey, v as verifyBackendJwtRS256 } from '../index-WbfzvmOt.js';
2
+ import 'express';
3
+ import 'jsonwebtoken';
@@ -0,0 +1,12 @@
1
+ import {
2
+ createAuthMiddleware,
3
+ readRs256PublicKey,
4
+ verifyBackendJwtRS256
5
+ } from "../chunk-W23UYULS.js";
6
+ import "../chunk-PZ5AY32C.js";
7
+ export {
8
+ createAuthMiddleware,
9
+ readRs256PublicKey,
10
+ verifyBackendJwtRS256
11
+ };
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -4,6 +4,7 @@ import {
4
4
  getOrSet,
5
5
  getTwoLevelCache
6
6
  } from "../chunk-IYFWQDHD.js";
7
+ import "../chunk-PZ5AY32C.js";
7
8
  export {
8
9
  TwoLevelCache,
9
10
  closeCache,
@@ -0,0 +1,10 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
10
+ //# sourceMappingURL=chunk-PZ5AY32C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,156 @@
1
+ import {
2
+ __export
3
+ } from "./chunk-PZ5AY32C.js";
4
+
5
+ // src/auth/index.ts
6
+ var auth_exports = {};
7
+ __export(auth_exports, {
8
+ createAuthMiddleware: () => createAuthMiddleware,
9
+ readRs256PublicKey: () => readRs256PublicKey,
10
+ verifyBackendJwtRS256: () => verifyBackendJwtRS256
11
+ });
12
+
13
+ // src/auth/jwt.ts
14
+ import fs from "fs";
15
+ import jwt from "jsonwebtoken";
16
+ function readFileIfExists(path) {
17
+ if (!path) return null;
18
+ try {
19
+ const v = fs.readFileSync(path, "utf8").trim();
20
+ return v.length ? v : null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ function readRs256PublicKey() {
26
+ const fromFile = readFileIfExists(process.env.JWT_PUBLIC_KEY_PATH);
27
+ if (fromFile) return fromFile;
28
+ const fromEnv = String(process.env.AUTH_JWT_PUBLIC_KEY || process.env.AUTH_RSA_PUBLIC_KEY || "").replace(/\\n/g, "\n").trim();
29
+ if (fromEnv) return fromEnv;
30
+ throw new Error("Missing RS256 public key (JWT_PUBLIC_KEY_PATH / AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY)");
31
+ }
32
+ function verifyBackendJwtRS256(raw) {
33
+ const publicKey = readRs256PublicKey();
34
+ const audience = process.env.JWT_AUDIENCE || process.env.AUTH_JWT_AUDIENCE || "getmarket.api";
35
+ const issuer = process.env.JWT_ISSUER || process.env.AUTH_JWT_ISSUER || "getmarket-auth";
36
+ return jwt.verify(raw, publicKey, {
37
+ algorithms: ["RS256"],
38
+ audience,
39
+ issuer
40
+ });
41
+ }
42
+
43
+ // src/auth/middleware.ts
44
+ function getBearerToken(req) {
45
+ const auth = String(req.headers?.authorization || "");
46
+ if (!auth.startsWith("Bearer ")) return null;
47
+ const token = auth.slice(7).trim();
48
+ return token.length ? token : null;
49
+ }
50
+ function normalizeUid(v) {
51
+ const s = String(v ?? "").trim();
52
+ return s.length ? s : null;
53
+ }
54
+ function createAuthMiddleware(opts) {
55
+ const {
56
+ subject,
57
+ allowFirebaseIdToken = false,
58
+ requireSubject = true,
59
+ hydrate
60
+ } = opts;
61
+ return async (req, res, next) => {
62
+ const token = getBearerToken(req);
63
+ if (!token) {
64
+ return res.status(401).json({
65
+ ok: false,
66
+ code: "AUTH_MISSING_TOKEN",
67
+ message: "Missing Authorization Bearer token"
68
+ });
69
+ }
70
+ const headerCtx = req.context || {};
71
+ const company_uid = normalizeUid(headerCtx.company_uid);
72
+ const branch_uid = normalizeUid(headerCtx.branch_uid);
73
+ try {
74
+ const decoded = verifyBackendJwtRS256(token);
75
+ const baseCtx = {
76
+ tokenType: "backend",
77
+ subject,
78
+ company_uid: company_uid ?? void 0,
79
+ branch_uid: branch_uid ?? void 0,
80
+ roles: Array.isArray(decoded?.roles) ? decoded.roles : [],
81
+ permissions: Array.isArray(decoded?.permissions) ? decoded.permissions : [],
82
+ denied_permissions: Array.isArray(decoded?.denied_permissions) ? decoded.denied_permissions : [],
83
+ session: {
84
+ jti: decoded?.jti,
85
+ device_id: decoded?.device_id,
86
+ expires_at: decoded?.exp
87
+ }
88
+ };
89
+ const hydrated = await hydrate({ decoded, req, subject, company_uid, branch_uid });
90
+ Object.assign(baseCtx, hydrated);
91
+ if (requireSubject) {
92
+ if (subject === "employee" && !baseCtx.employee) {
93
+ return res.status(401).json({
94
+ ok: false,
95
+ code: "AUTH_EMPLOYEE_NOT_FOUND",
96
+ message: "Employee not resolved by hydrator"
97
+ });
98
+ }
99
+ if (subject === "customer" && !baseCtx.customer) {
100
+ return res.status(401).json({
101
+ ok: false,
102
+ code: "AUTH_CUSTOMER_NOT_FOUND",
103
+ message: "Customer not resolved by hydrator"
104
+ });
105
+ }
106
+ }
107
+ req.auth = baseCtx;
108
+ return next();
109
+ } catch (errJwt) {
110
+ if (!allowFirebaseIdToken) {
111
+ return res.status(401).json({
112
+ ok: false,
113
+ code: "AUTH_INVALID_TOKEN",
114
+ message: "Invalid or expired token"
115
+ });
116
+ }
117
+ try {
118
+ const { default: admin } = await import("firebase-admin");
119
+ const firebaseDecoded = await admin.auth().verifyIdToken(token);
120
+ if (firebaseDecoded.email && firebaseDecoded.email_verified === false) {
121
+ return res.status(401).json({
122
+ ok: false,
123
+ code: "AUTH_EMAIL_NOT_VERIFIED",
124
+ message: "Email not verified"
125
+ });
126
+ }
127
+ req.auth = {
128
+ tokenType: "backend",
129
+ subject,
130
+ firebase: firebaseDecoded,
131
+ company_uid: company_uid ?? void 0,
132
+ branch_uid: branch_uid ?? void 0,
133
+ companies: [],
134
+ roles: [],
135
+ permissions: [],
136
+ denied_permissions: []
137
+ };
138
+ return next();
139
+ } catch {
140
+ return res.status(401).json({
141
+ ok: false,
142
+ code: "AUTH_INVALID_TOKEN",
143
+ message: "Invalid or expired token"
144
+ });
145
+ }
146
+ }
147
+ };
148
+ }
149
+
150
+ export {
151
+ readRs256PublicKey,
152
+ verifyBackendJwtRS256,
153
+ createAuthMiddleware,
154
+ auth_exports
155
+ };
156
+ //# sourceMappingURL=chunk-W23UYULS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/index.ts","../src/auth/jwt.ts","../src/auth/middleware.ts"],"sourcesContent":["export * from \"./types\";\nexport * from \"./jwt\";\nexport * from \"./middleware\";\n","import fs from \"fs\";\nimport jwt, {JwtPayload} from \"jsonwebtoken\";\n\nfunction readFileIfExists(path?: string): string | null {\n if (!path) return null;\n try {\n const v = fs.readFileSync(path, \"utf8\").trim();\n return v.length ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * ✅ Keys viven en getmarket-stack:\n * - JWT_PUBLIC_KEY_PATH=/run/secrets/jwtRS256.key.pub (recomendado)\n * - fallback env AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY\n */\nexport function readRs256PublicKey(): string {\n const fromFile = readFileIfExists(process.env.JWT_PUBLIC_KEY_PATH);\n if (fromFile) return fromFile;\n\n const fromEnv = String(process.env.AUTH_JWT_PUBLIC_KEY || process.env.AUTH_RSA_PUBLIC_KEY || \"\")\n .replace(/\\\\n/g, \"\\n\")\n .trim();\n\n if (fromEnv) return fromEnv;\n\n throw new Error(\"Missing RS256 public key (JWT_PUBLIC_KEY_PATH / AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY)\");\n}\n\nexport function verifyBackendJwtRS256(raw: string): JwtPayload {\n const publicKey = readRs256PublicKey();\n\n const audience = process.env.JWT_AUDIENCE || process.env.AUTH_JWT_AUDIENCE || \"getmarket.api\";\n const issuer = process.env.JWT_ISSUER || process.env.AUTH_JWT_ISSUER || \"getmarket-auth\";\n\n // ✅ SOLO RS256\n return jwt.verify(raw, publicKey, {\n algorithms: [\"RS256\"],\n audience,\n issuer,\n }) as JwtPayload;\n}\n","import type {NextFunction, Response} from \"express\";\nimport {verifyBackendJwtRS256} from \"./jwt\";\nimport type {AuthContext, AuthMiddlewareOptions} from \"./types\";\n\nfunction getBearerToken(req: any): string | null {\n const auth = String(req.headers?.authorization || \"\");\n if (!auth.startsWith(\"Bearer \")) return null;\n const token = auth.slice(7).trim();\n return token.length ? token : null;\n}\n\nfunction normalizeUid(v: any): string | null {\n const s = String(v ?? \"\").trim();\n return s.length ? s : null;\n}\n\n/**\n * ✅ Middleware estándar:\n * - Solo Authorization: Bearer\n * - Solo RS256\n * - Cero legacy\n * - Hidrata vía hook (OBLIGATORIO) para que cada micro no replique lógica\n */\nexport function createAuthMiddleware(opts: AuthMiddlewareOptions) {\n const {\n subject,\n allowFirebaseIdToken = false,\n requireSubject = true,\n hydrate,\n } = opts;\n\n return async (req: any, res: Response, next: NextFunction) => {\n const token = getBearerToken(req);\n if (!token) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_MISSING_TOKEN\",\n message: \"Missing Authorization Bearer token\",\n });\n }\n\n // Contexto desde parseHeaders (SDK) -> req.context\n const headerCtx = (req as any).context || {};\n const company_uid = normalizeUid(headerCtx.company_uid);\n const branch_uid = normalizeUid(headerCtx.branch_uid);\n\n // 1) RS256 backend JWT\n try {\n const decoded: any = verifyBackendJwtRS256(token);\n\n const baseCtx: AuthContext = {\n tokenType: \"backend\",\n subject,\n company_uid: company_uid ?? undefined,\n branch_uid: branch_uid ?? undefined,\n roles: Array.isArray(decoded?.roles) ? decoded.roles : [],\n permissions: Array.isArray(decoded?.permissions) ? decoded.permissions : [],\n denied_permissions: Array.isArray(decoded?.denied_permissions) ? decoded.denied_permissions : [],\n session: {\n jti: decoded?.jti,\n device_id: decoded?.device_id,\n expires_at: decoded?.exp,\n },\n };\n\n // ✅ hydrate obligatorio (cero legacy)\n const hydrated = await hydrate({decoded, req, subject, company_uid, branch_uid});\n Object.assign(baseCtx, hydrated);\n\n if (requireSubject) {\n if (subject === \"employee\" && !baseCtx.employee) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_EMPLOYEE_NOT_FOUND\",\n message: \"Employee not resolved by hydrator\",\n });\n }\n if (subject === \"customer\" && !baseCtx.customer) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_CUSTOMER_NOT_FOUND\",\n message: \"Customer not resolved by hydrator\",\n });\n }\n }\n\n req.auth = baseCtx;\n return next();\n } catch (errJwt) {\n // 2) Firebase opcional (si está habilitado explícitamente)\n if (!allowFirebaseIdToken) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_INVALID_TOKEN\",\n message: \"Invalid or expired token\",\n });\n }\n\n try {\n // Import dinámico (pero firebase-admin está en deps del SDK)\n const {default: admin} = await import(\"firebase-admin\");\n const firebaseDecoded = await admin.auth().verifyIdToken(token);\n\n if (firebaseDecoded.email && firebaseDecoded.email_verified === false) {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_EMAIL_NOT_VERIFIED\",\n message: \"Email not verified\",\n });\n }\n\n req.auth = {\n tokenType: \"backend\",\n subject,\n firebase: firebaseDecoded,\n company_uid: company_uid ?? undefined,\n branch_uid: branch_uid ?? undefined,\n companies: [],\n roles: [],\n permissions: [],\n denied_permissions: [],\n };\n\n return next();\n } catch {\n return res.status(401).json({\n ok: false,\n code: \"AUTH_INVALID_TOKEN\",\n message: \"Invalid or expired token\",\n });\n }\n }\n };\n}\n"],"mappings":"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","names":[]}
@@ -96,11 +96,54 @@ function internalAuth(req, res, next) {
96
96
  return next();
97
97
  }
98
98
 
99
+ // src/middlewares/autorization.ts
100
+ function getAuth(req) {
101
+ return req.auth ?? {};
102
+ }
103
+ function requireAuthContext() {
104
+ return (req, res, next) => {
105
+ if (!req.auth) {
106
+ return sendError(req, res, 401, "UNAUTHORIZED", "Missing auth context");
107
+ }
108
+ return next();
109
+ };
110
+ }
111
+ function requirePermissions(...perms) {
112
+ return (req, res, next) => {
113
+ const auth = getAuth(req);
114
+ const allow = new Set(auth.permissions ?? []);
115
+ const deny = new Set(auth.denied_permissions ?? []);
116
+ for (const p of perms) {
117
+ if (deny.has(p)) {
118
+ return sendError(req, res, 403, "FORBIDDEN", `Denied: ${p}`);
119
+ }
120
+ }
121
+ const missing = perms.filter((p) => !allow.has(p));
122
+ if (missing.length) {
123
+ return sendError(req, res, 403, "FORBIDDEN", "Missing permissions", { missing });
124
+ }
125
+ return next();
126
+ };
127
+ }
128
+ function requireRoles(...roles) {
129
+ return (req, res, next) => {
130
+ const auth = getAuth(req);
131
+ const have = new Set(auth.roles ?? []);
132
+ if (!roles.some((r) => have.has(r))) {
133
+ return sendError(req, res, 403, "FORBIDDEN", "Role not allowed", { required: roles });
134
+ }
135
+ return next();
136
+ };
137
+ }
138
+
99
139
  export {
100
140
  requestId,
101
141
  parseHeaders,
102
142
  sendOk,
103
143
  sendError,
104
- internalAuth
144
+ internalAuth,
145
+ requireAuthContext,
146
+ requirePermissions,
147
+ requireRoles
105
148
  };
106
- //# sourceMappingURL=chunk-HTHX24NK.js.map
149
+ //# sourceMappingURL=chunk-Y2JJLHAY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/middlewares/requestId.ts","../src/middlewares/parseHeaders.ts","../src/middlewares/internalAuth.ts","../src/middlewares/respond.ts","../src/middlewares/autorization.ts"],"sourcesContent":["// middlewares/requestId.ts\nimport type {Request, Response, NextFunction} from \"express\";\nimport {randomUUID, randomBytes} from \"crypto\";\n\nexport const REQUEST_ID_HEADER = \"x-request-id\";\nexport const REQUEST_ID_HEADER_ALT = \"x-requestid\";\nexport const RESPONSE_REQUEST_ID_HEADER = \"X-Request-Id\";\n\n// Si quieres IDs más cortos (opcional). Por defecto usamos UUID.\nfunction nanoidLike(len = 21) {\n return randomBytes(16).toString(\"base64url\").slice(0, len);\n}\n\nexport default function requestId(req: Request, res: Response, next: NextFunction) {\n const headerId = (req.headers[REQUEST_ID_HEADER] || req.headers[REQUEST_ID_HEADER_ALT]) as\n | string\n | undefined;\n\n // ✅ estándar único: usa UUID (o cambia a nanoidLike() si prefieres corto)\n const id = headerId?.trim() || randomUUID();\n\n // ✅ estándar único (no legacy)\n (req as any).requestId = id;\n res.locals.requestId = id;\n\n // ✅ respuesta\n res.setHeader(RESPONSE_REQUEST_ID_HEADER, id);\n\n next();\n}\n","import type {Request, Response, NextFunction} from \"express\";\nimport {getRequestContextFromHeaders} from \"../headers\";\n\n/**\n * ✅ NO-LEGACY:\n * - solo setea UIDs\n * - no crea objetos company/branch\n * - no copia provider\n */\nexport default function parseHeaders(req: Request, _res: Response, next: NextFunction) {\n const context = getRequestContextFromHeaders(req.headers as any);\n\n (req as any).context = context;\n\n const auth: any = (req as any).auth ?? ((req as any).auth = {});\n if (context.company_uid) auth.company_uid = context.company_uid;\n if (context.branch_uid) auth.branch_uid = context.branch_uid;\n if (context.employee_uid) auth.employee_uid = context.employee_uid;\n\n next();\n}\n","import type {Request, Response, NextFunction} from \"express\";\nimport fs from \"fs\";\nimport crypto from \"crypto\";\nimport {sendError} from \"./respond\";\nimport {HEADER_INTERNAL_API_KEY} from \"../headers\";\n\nfunction readSecretFile(path?: string): string | null {\n if (!path) return null;\n try {\n const v = fs.readFileSync(path, \"utf8\").trim();\n return v.length ? v : null;\n } catch {\n return null;\n }\n}\n\nfunction splitKeys(v?: string | null): string[] {\n if (!v) return [];\n return v.split(\",\").map((s) => s.trim()).filter(Boolean);\n}\n\nfunction getExpectedKeys(): string[] {\n const fileKey = readSecretFile(process.env.INTERNAL_API_KEY_FILE);\n const envKey = (process.env.INTERNAL_API_KEY || \"\").trim();\n const raw = fileKey || envKey;\n return splitKeys(raw);\n}\n\nfunction extractToken(req: Request): string | null {\n const apiKey = (req.header(HEADER_INTERNAL_API_KEY) || \"\").trim();\n return apiKey || null;\n}\n\nfunction safeEquals(a: string, b: string): boolean {\n const aa = Buffer.from(a);\n const bb = Buffer.from(b);\n if (aa.length !== bb.length) return false;\n return crypto.timingSafeEqual(aa, bb);\n}\n\nexport default function internalAuth(req: Request, res: Response, next: NextFunction) {\n const token = extractToken(req);\n\n if (!token) {\n return sendError(req, res, 401, \"UNAUTHORIZED\", `Missing internal api key (${HEADER_INTERNAL_API_KEY})`);\n }\n\n const expectedKeys = getExpectedKeys();\n if (expectedKeys.length === 0) {\n return sendError(\n req,\n res,\n 500,\n \"MISCONFIGURED_INTERNAL_AUTH\",\n \"Internal api key not configured (INTERNAL_API_KEY or INTERNAL_API_KEY_FILE)\"\n );\n }\n\n const ok = expectedKeys.some((k) => safeEquals(token, k));\n if (!ok) {\n return sendError(req, res, 403, \"FORBIDDEN\", \"Invalid internal api key\");\n }\n\n return next();\n}\n","import type {Request, Response} from \"express\";\n\nexport function sendOk<T>(_req: Request, res: Response, data: T, statusCode = 200) {\n return res.status(statusCode).json({ok: true, data, requestId: res.locals?.requestId ?? null});\n}\n\nexport function sendError(\n _req: Request,\n res: Response,\n statusCode: number,\n code: string,\n message: string,\n details?: any\n) {\n return res.status(statusCode).json({\n ok: false,\n error: {code, message, ...(details !== undefined ? {details} : {})},\n requestId: res.locals?.requestId ?? null,\n });\n}\n","// packages/sdk/src/middlewares/authorization.ts\nimport type {Request, Response, NextFunction} from \"express\";\nimport {sendError} from \"./respond\";\n\ntype AuthShape = {\n roles?: string[];\n permissions?: string[];\n denied_permissions?: string[];\n};\n\nfunction getAuth(req: Request): AuthShape {\n return ((req as any).auth ?? {}) as AuthShape;\n}\n\n/**\n * 401 si no existe req.auth (contexto auth).\n * Útil para proteger rutas internas donde SIEMPRE debe existir auth.\n */\nexport function requireAuthContext() {\n return (req: Request, res: Response, next: NextFunction) => {\n if (!(req as any).auth) {\n return sendError(req, res, 401, \"UNAUTHORIZED\", \"Missing auth context\");\n }\n return next();\n };\n}\n\n/**\n * Requiere TODOS los permisos indicados.\n * Regla: denied_permissions siempre gana sobre permissions.\n */\nexport function requirePermissions(...perms: string[]) {\n return (req: Request, res: Response, next: NextFunction) => {\n const auth = getAuth(req);\n\n const allow = new Set<string>(auth.permissions ?? []);\n const deny = new Set<string>(auth.denied_permissions ?? []);\n\n // deny gana siempre\n for (const p of perms) {\n if (deny.has(p)) {\n return sendError(req, res, 403, \"FORBIDDEN\", `Denied: ${p}`);\n }\n }\n\n const missing = perms.filter((p) => !allow.has(p));\n if (missing.length) {\n return sendError(req, res, 403, \"FORBIDDEN\", \"Missing permissions\", {missing});\n }\n\n return next();\n };\n}\n\n/**\n * Requiere al menos 1 de los roles indicados.\n */\nexport function requireRoles(...roles: string[]) {\n return (req: Request, res: Response, next: NextFunction) => {\n const auth = getAuth(req);\n const have = new Set<string>(auth.roles ?? []);\n\n if (!roles.some((r) => have.has(r))) {\n return sendError(req, res, 403, \"FORBIDDEN\", \"Role not allowed\", {required: roles});\n }\n\n return next();\n };\n}\n"],"mappings":"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","names":[]}
@@ -7,6 +7,7 @@ import {
7
7
  withRequestId,
8
8
  withRequestIdConfig
9
9
  } from "../chunk-GG7EI74E.js";
10
+ import "../chunk-PZ5AY32C.js";
10
11
  export {
11
12
  InternalHttp,
12
13
  REQUEST_ID_HEADER,
@@ -7,6 +7,7 @@ import {
7
7
  HEADER_REQUEST_ID,
8
8
  getRequestContextFromHeaders
9
9
  } from "../chunk-65HACONF.js";
10
+ import "../chunk-PZ5AY32C.js";
10
11
  export {
11
12
  HEADER_AUTHORIZATION,
12
13
  HEADER_BRANCH_UID,
@@ -0,0 +1,87 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { JwtPayload } from 'jsonwebtoken';
3
+
4
+ type AuthSubject = "employee" | "customer";
5
+ type TokenType = "backend";
6
+ type AuthSession = {
7
+ jti?: string;
8
+ device_id?: string;
9
+ expires_at?: number;
10
+ };
11
+ type AuthContext = {
12
+ tokenType: TokenType;
13
+ subject: AuthSubject;
14
+ employee?: any;
15
+ customer?: any;
16
+ company_uid?: string;
17
+ branch_uid?: string;
18
+ companies?: any[];
19
+ company?: any;
20
+ branch?: any;
21
+ roles?: string[];
22
+ permissions?: string[];
23
+ denied_permissions?: string[];
24
+ session?: AuthSession;
25
+ firebase?: any;
26
+ };
27
+ type HydrateInput = {
28
+ decoded: any;
29
+ req: Request;
30
+ subject: AuthSubject;
31
+ company_uid: string | null;
32
+ branch_uid: string | null;
33
+ };
34
+ type HydrateResult = Partial<Pick<AuthContext, "employee" | "customer" | "companies" | "company" | "branch" | "roles" | "permissions" | "denied_permissions">>;
35
+ type Hydrator = (input: HydrateInput) => Promise<HydrateResult> | HydrateResult;
36
+ type AuthMiddlewareOptions = {
37
+ subject: AuthSubject;
38
+ /**
39
+ * ✅ Si true, exige que el sujeto (employee/customer) exista tras hydrate.
40
+ * Default: true
41
+ */
42
+ requireSubject?: boolean;
43
+ /**
44
+ * Si true, permite fallback a Firebase idToken.
45
+ * Default: false
46
+ */
47
+ allowFirebaseIdToken?: boolean;
48
+ /**
49
+ * ✅ OBLIGATORIO para evitar "legacy" o acoplamientos:
50
+ * el micro decide cómo hidratar (DB local / AuthClient / etc).
51
+ */
52
+ hydrate: Hydrator;
53
+ };
54
+
55
+ /**
56
+ * ✅ Keys viven en getmarket-stack:
57
+ * - JWT_PUBLIC_KEY_PATH=/run/secrets/jwtRS256.key.pub (recomendado)
58
+ * - fallback env AUTH_JWT_PUBLIC_KEY / AUTH_RSA_PUBLIC_KEY
59
+ */
60
+ declare function readRs256PublicKey(): string;
61
+ declare function verifyBackendJwtRS256(raw: string): JwtPayload;
62
+
63
+ /**
64
+ * ✅ Middleware estándar:
65
+ * - Solo Authorization: Bearer
66
+ * - Solo RS256
67
+ * - Cero legacy
68
+ * - Hidrata vía hook (OBLIGATORIO) para que cada micro no replique lógica
69
+ */
70
+ declare function createAuthMiddleware(opts: AuthMiddlewareOptions): (req: any, res: Response, next: NextFunction) => Promise<void | Response<any, Record<string, any>>>;
71
+
72
+ type index_AuthContext = AuthContext;
73
+ type index_AuthMiddlewareOptions = AuthMiddlewareOptions;
74
+ type index_AuthSession = AuthSession;
75
+ type index_AuthSubject = AuthSubject;
76
+ type index_HydrateInput = HydrateInput;
77
+ type index_HydrateResult = HydrateResult;
78
+ type index_Hydrator = Hydrator;
79
+ type index_TokenType = TokenType;
80
+ declare const index_createAuthMiddleware: typeof createAuthMiddleware;
81
+ declare const index_readRs256PublicKey: typeof readRs256PublicKey;
82
+ declare const index_verifyBackendJwtRS256: typeof verifyBackendJwtRS256;
83
+ declare namespace index {
84
+ export { type index_AuthContext as AuthContext, type index_AuthMiddlewareOptions as AuthMiddlewareOptions, type index_AuthSession as AuthSession, type index_AuthSubject as AuthSubject, type index_HydrateInput as HydrateInput, type index_HydrateResult as HydrateResult, type index_Hydrator as Hydrator, type index_TokenType as TokenType, index_createAuthMiddleware as createAuthMiddleware, index_readRs256PublicKey as readRs256PublicKey, index_verifyBackendJwtRS256 as verifyBackendJwtRS256 };
85
+ }
86
+
87
+ export { type AuthContext as A, type HydrateInput as H, type TokenType as T, type AuthMiddlewareOptions as a, type AuthSession as b, type AuthSubject as c, type HydrateResult as d, type Hydrator as e, createAuthMiddleware as f, index as i, readRs256PublicKey as r, verifyBackendJwtRS256 as v };