organify-auth-sdk 0.2.1 → 0.2.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.
- package/dist/index.cjs +271 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +152 -0
- package/dist/nestjs/index.cjs +7877 -0
- package/dist/nestjs/index.cjs.map +1 -0
- package/dist/nestjs/index.d.cts +106 -0
- package/package.json +7 -4
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jwt = require('jsonwebtoken');
|
|
4
|
+
var msgpack = require('@msgpack/msgpack');
|
|
5
|
+
|
|
6
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
7
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
|
+
|
|
9
|
+
var jwt__default = /*#__PURE__*/_interopDefault(jwt);
|
|
10
|
+
|
|
11
|
+
// src/types.ts
|
|
12
|
+
var Permission = /* @__PURE__ */ ((Permission2) => {
|
|
13
|
+
Permission2["READ_OWN_PROFILE"] = "read:own_profile";
|
|
14
|
+
Permission2["UPDATE_OWN_PROFILE"] = "update:own_profile";
|
|
15
|
+
Permission2["READ_USERS"] = "read:users";
|
|
16
|
+
Permission2["CREATE_USERS"] = "create:users";
|
|
17
|
+
Permission2["UPDATE_USERS"] = "update:users";
|
|
18
|
+
Permission2["DELETE_USERS"] = "delete:users";
|
|
19
|
+
Permission2["READ_PROJECTS"] = "read:projects";
|
|
20
|
+
Permission2["CREATE_PROJECTS"] = "create:projects";
|
|
21
|
+
Permission2["UPDATE_PROJECTS"] = "update:projects";
|
|
22
|
+
Permission2["DELETE_PROJECTS"] = "delete:projects";
|
|
23
|
+
Permission2["READ_TASKS"] = "read:tasks";
|
|
24
|
+
Permission2["CREATE_TASKS"] = "create:tasks";
|
|
25
|
+
Permission2["UPDATE_TASKS"] = "update:tasks";
|
|
26
|
+
Permission2["DELETE_TASKS"] = "delete:tasks";
|
|
27
|
+
Permission2["READ_TEAMS"] = "read:teams";
|
|
28
|
+
Permission2["CREATE_TEAMS"] = "create:teams";
|
|
29
|
+
Permission2["UPDATE_TEAMS"] = "update:teams";
|
|
30
|
+
Permission2["DELETE_TEAMS"] = "delete:teams";
|
|
31
|
+
Permission2["MANAGE_TEAM_MEMBERS"] = "manage:team_members";
|
|
32
|
+
Permission2["READ_REPORTS"] = "read:reports";
|
|
33
|
+
Permission2["CREATE_REPORTS"] = "create:reports";
|
|
34
|
+
Permission2["USE_AI"] = "use:ai";
|
|
35
|
+
Permission2["READ_PLANS"] = "read:plans";
|
|
36
|
+
Permission2["CREATE_PLANS"] = "create:plans";
|
|
37
|
+
Permission2["UPDATE_PLANS"] = "update:plans";
|
|
38
|
+
Permission2["DELETE_PLANS"] = "delete:plans";
|
|
39
|
+
Permission2["MANAGE_INTEGRATIONS"] = "manage:integrations";
|
|
40
|
+
Permission2["MANAGE_AUTOMATIONS"] = "manage:automations";
|
|
41
|
+
Permission2["VIEW_ANALYTICS"] = "view:analytics";
|
|
42
|
+
return Permission2;
|
|
43
|
+
})(Permission || {});
|
|
44
|
+
var RolePermissions = {
|
|
45
|
+
admin: [...Object.values(Permission)],
|
|
46
|
+
manager: [
|
|
47
|
+
"read:own_profile" /* READ_OWN_PROFILE */,
|
|
48
|
+
"update:own_profile" /* UPDATE_OWN_PROFILE */,
|
|
49
|
+
"read:projects" /* READ_PROJECTS */,
|
|
50
|
+
"create:projects" /* CREATE_PROJECTS */,
|
|
51
|
+
"update:projects" /* UPDATE_PROJECTS */,
|
|
52
|
+
"read:tasks" /* READ_TASKS */,
|
|
53
|
+
"create:tasks" /* CREATE_TASKS */,
|
|
54
|
+
"update:tasks" /* UPDATE_TASKS */,
|
|
55
|
+
"delete:tasks" /* DELETE_TASKS */,
|
|
56
|
+
"read:teams" /* READ_TEAMS */,
|
|
57
|
+
"create:teams" /* CREATE_TEAMS */,
|
|
58
|
+
"update:teams" /* UPDATE_TEAMS */,
|
|
59
|
+
"manage:team_members" /* MANAGE_TEAM_MEMBERS */,
|
|
60
|
+
"read:reports" /* READ_REPORTS */,
|
|
61
|
+
"create:reports" /* CREATE_REPORTS */,
|
|
62
|
+
"use:ai" /* USE_AI */,
|
|
63
|
+
"view:analytics" /* VIEW_ANALYTICS */
|
|
64
|
+
],
|
|
65
|
+
user: [
|
|
66
|
+
"read:own_profile" /* READ_OWN_PROFILE */,
|
|
67
|
+
"update:own_profile" /* UPDATE_OWN_PROFILE */,
|
|
68
|
+
"read:projects" /* READ_PROJECTS */,
|
|
69
|
+
"create:projects" /* CREATE_PROJECTS */,
|
|
70
|
+
"update:projects" /* UPDATE_PROJECTS */,
|
|
71
|
+
"read:tasks" /* READ_TASKS */,
|
|
72
|
+
"create:tasks" /* CREATE_TASKS */,
|
|
73
|
+
"update:tasks" /* UPDATE_TASKS */,
|
|
74
|
+
"delete:tasks" /* DELETE_TASKS */,
|
|
75
|
+
"read:teams" /* READ_TEAMS */,
|
|
76
|
+
"create:teams" /* CREATE_TEAMS */,
|
|
77
|
+
"read:reports" /* READ_REPORTS */,
|
|
78
|
+
"use:ai" /* USE_AI */
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
function verifyToken(token, options) {
|
|
82
|
+
return jwt__default.default.verify(token, options.secret, {
|
|
83
|
+
ignoreExpiration: options.ignoreExpiration
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function decodeToken(token) {
|
|
87
|
+
return jwt__default.default.decode(token);
|
|
88
|
+
}
|
|
89
|
+
function extractBearerToken(authHeader) {
|
|
90
|
+
if (!authHeader?.startsWith("Bearer ")) return null;
|
|
91
|
+
return authHeader.substring(7);
|
|
92
|
+
}
|
|
93
|
+
function extractCookieToken(cookieHeader, cookieName = "auth_token") {
|
|
94
|
+
if (!cookieHeader) return null;
|
|
95
|
+
const match = cookieHeader.split(";").map((s) => s.trim()).find((s) => s.startsWith(`${cookieName}=`));
|
|
96
|
+
if (!match) return null;
|
|
97
|
+
return decodeURIComponent(match.split("=")[1]);
|
|
98
|
+
}
|
|
99
|
+
function hasPermission(role, permission, rolePermissions) {
|
|
100
|
+
const perms = rolePermissions[role];
|
|
101
|
+
if (!perms) return false;
|
|
102
|
+
return perms.includes(permission);
|
|
103
|
+
}
|
|
104
|
+
function validateServiceToken(token, expectedToken) {
|
|
105
|
+
if (!token) return false;
|
|
106
|
+
return token === expectedToken;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/fb-token.ts
|
|
110
|
+
var WINDOW_MS = 30 * 60 * 1e3;
|
|
111
|
+
var cachedToken = null;
|
|
112
|
+
var cachedWindowIndex = -1;
|
|
113
|
+
function getTimeWindowIndex() {
|
|
114
|
+
return Math.floor(Date.now() / WINDOW_MS);
|
|
115
|
+
}
|
|
116
|
+
async function hmacSHA512(secret, message) {
|
|
117
|
+
const encoder = new TextEncoder();
|
|
118
|
+
const keyData = encoder.encode(secret);
|
|
119
|
+
const msgData = encoder.encode(message);
|
|
120
|
+
const key = await crypto.subtle.importKey(
|
|
121
|
+
"raw",
|
|
122
|
+
keyData,
|
|
123
|
+
{ name: "HMAC", hash: "SHA-512" },
|
|
124
|
+
false,
|
|
125
|
+
["sign"]
|
|
126
|
+
);
|
|
127
|
+
const signature = await crypto.subtle.sign("HMAC", key, msgData);
|
|
128
|
+
const bytes = new Uint8Array(signature);
|
|
129
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
130
|
+
}
|
|
131
|
+
async function getFBToken(secret) {
|
|
132
|
+
const windowIndex = getTimeWindowIndex();
|
|
133
|
+
if (cachedToken && cachedWindowIndex === windowIndex) {
|
|
134
|
+
return cachedToken;
|
|
135
|
+
}
|
|
136
|
+
const resolvedSecret = secret || typeof process !== "undefined" && process.env?.NEXT_PUBLIC_FB_TOKEN_SECRET || typeof ({ url: (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)) }) !== "undefined" && undefined?.VITE_FB_TOKEN_SECRET || "";
|
|
137
|
+
if (!resolvedSecret) {
|
|
138
|
+
console.warn("[FBToken] No FB_TOKEN_SECRET configured");
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
const token = await hmacSHA512(
|
|
142
|
+
resolvedSecret,
|
|
143
|
+
`organify-fb-v1:${windowIndex}`
|
|
144
|
+
);
|
|
145
|
+
cachedToken = token;
|
|
146
|
+
cachedWindowIndex = windowIndex;
|
|
147
|
+
return token;
|
|
148
|
+
}
|
|
149
|
+
async function createFBTokenHeaders(secret) {
|
|
150
|
+
const token = await getFBToken(secret);
|
|
151
|
+
if (!token) return {};
|
|
152
|
+
return { "X-FB-Token": token };
|
|
153
|
+
}
|
|
154
|
+
function fbTokenAxiosInterceptor(secret) {
|
|
155
|
+
return async (config) => {
|
|
156
|
+
const token = await getFBToken(secret);
|
|
157
|
+
if (token) {
|
|
158
|
+
config.headers = config.headers || {};
|
|
159
|
+
config.headers["X-FB-Token"] = token;
|
|
160
|
+
}
|
|
161
|
+
return config;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
var MSGPACK_MIME = "application/x-msgpack";
|
|
165
|
+
var ServiceClient = class {
|
|
166
|
+
config;
|
|
167
|
+
constructor(config) {
|
|
168
|
+
this.config = {
|
|
169
|
+
serviceToken: config.serviceToken,
|
|
170
|
+
timeout: config.timeout ?? 1e4,
|
|
171
|
+
headers: config.headers ?? {}
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// ─── HTTP Methods ─────────────────────────
|
|
175
|
+
async get(url, opts) {
|
|
176
|
+
return this.request("GET", url, void 0, opts);
|
|
177
|
+
}
|
|
178
|
+
async post(url, body, opts) {
|
|
179
|
+
return this.request("POST", url, body, opts);
|
|
180
|
+
}
|
|
181
|
+
async put(url, body, opts) {
|
|
182
|
+
return this.request("PUT", url, body, opts);
|
|
183
|
+
}
|
|
184
|
+
async patch(url, body, opts) {
|
|
185
|
+
return this.request("PATCH", url, body, opts);
|
|
186
|
+
}
|
|
187
|
+
async delete(url, opts) {
|
|
188
|
+
return this.request("DELETE", url, void 0, opts);
|
|
189
|
+
}
|
|
190
|
+
// ─── Core Request ─────────────────────────
|
|
191
|
+
async request(method, url, body, opts) {
|
|
192
|
+
const timeout = opts?.timeout ?? this.config.timeout;
|
|
193
|
+
const controller = new AbortController();
|
|
194
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
195
|
+
try {
|
|
196
|
+
const headers = {
|
|
197
|
+
// Always use MessagePack for internal communication
|
|
198
|
+
"Accept": MSGPACK_MIME,
|
|
199
|
+
"X-Service-Token": this.config.serviceToken,
|
|
200
|
+
...this.config.headers,
|
|
201
|
+
...opts?.headers
|
|
202
|
+
};
|
|
203
|
+
if (opts?.userId) {
|
|
204
|
+
headers["x-user-id"] = opts.userId;
|
|
205
|
+
}
|
|
206
|
+
const fetchOpts = {
|
|
207
|
+
method,
|
|
208
|
+
headers,
|
|
209
|
+
signal: controller.signal
|
|
210
|
+
};
|
|
211
|
+
if (body !== void 0 && body !== null) {
|
|
212
|
+
const packed = msgpack.encode(body);
|
|
213
|
+
fetchOpts.body = Buffer.from(packed.buffer, packed.byteOffset, packed.byteLength);
|
|
214
|
+
headers["Content-Type"] = MSGPACK_MIME;
|
|
215
|
+
}
|
|
216
|
+
const response = await fetch(url, fetchOpts);
|
|
217
|
+
const contentType = response.headers.get("content-type") || "";
|
|
218
|
+
let data;
|
|
219
|
+
if (/msgpack/i.test(contentType)) {
|
|
220
|
+
const buffer = await response.arrayBuffer();
|
|
221
|
+
data = msgpack.decode(new Uint8Array(buffer));
|
|
222
|
+
} else {
|
|
223
|
+
const text = await response.text();
|
|
224
|
+
try {
|
|
225
|
+
data = JSON.parse(text);
|
|
226
|
+
} catch {
|
|
227
|
+
data = text;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const responseHeaders = {};
|
|
231
|
+
response.headers.forEach((value, key) => {
|
|
232
|
+
responseHeaders[key] = value;
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok) {
|
|
235
|
+
throw new ServiceClientError(
|
|
236
|
+
`Service request failed: ${method} ${url} \u2192 ${response.status}`,
|
|
237
|
+
response.status,
|
|
238
|
+
data
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return { data, status: response.status, headers: responseHeaders };
|
|
242
|
+
} finally {
|
|
243
|
+
clearTimeout(timer);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var ServiceClientError = class extends Error {
|
|
248
|
+
constructor(message, status, response) {
|
|
249
|
+
super(message);
|
|
250
|
+
this.status = status;
|
|
251
|
+
this.response = response;
|
|
252
|
+
this.name = "ServiceClientError";
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
exports.MSGPACK_MIME = MSGPACK_MIME;
|
|
257
|
+
exports.Permission = Permission;
|
|
258
|
+
exports.RolePermissions = RolePermissions;
|
|
259
|
+
exports.ServiceClient = ServiceClient;
|
|
260
|
+
exports.ServiceClientError = ServiceClientError;
|
|
261
|
+
exports.createFBTokenHeaders = createFBTokenHeaders;
|
|
262
|
+
exports.decodeToken = decodeToken;
|
|
263
|
+
exports.extractBearerToken = extractBearerToken;
|
|
264
|
+
exports.extractCookieToken = extractCookieToken;
|
|
265
|
+
exports.fbTokenAxiosInterceptor = fbTokenAxiosInterceptor;
|
|
266
|
+
exports.getFBToken = getFBToken;
|
|
267
|
+
exports.hasPermission = hasPermission;
|
|
268
|
+
exports.validateServiceToken = validateServiceToken;
|
|
269
|
+
exports.verifyToken = verifyToken;
|
|
270
|
+
//# sourceMappingURL=index.cjs.map
|
|
271
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/jwt.ts","../src/fb-token.ts","../src/service-client.ts"],"names":["Permission","jwt","encode","decode"],"mappings":"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","file":"index.cjs","sourcesContent":["// ─────────────────────────────────────────────\n// @organify/auth-sdk — Shared Auth Types\n// ─────────────────────────────────────────────\n\nexport interface JwtPayload {\n /** User ID (subject) */\n sub: number;\n email: string;\n name: string;\n role: 'admin' | 'user' | 'manager';\n plan: string;\n teamIds: number[];\n /** Token type — only present on refresh tokens */\n tokenType?: 'refresh';\n iat?: number;\n exp?: number;\n}\n\nexport interface AuthenticatedUser {\n userId: number;\n email: string;\n name: string;\n role: string;\n plan: string;\n}\n\nexport interface JwtTokens {\n accessToken: string;\n refreshToken: string;\n}\n\n// ─── Permissions ────────────────────────────\n\nexport enum Permission {\n READ_OWN_PROFILE = 'read:own_profile',\n UPDATE_OWN_PROFILE = 'update:own_profile',\n READ_USERS = 'read:users',\n CREATE_USERS = 'create:users',\n UPDATE_USERS = 'update:users',\n DELETE_USERS = 'delete:users',\n READ_PROJECTS = 'read:projects',\n CREATE_PROJECTS = 'create:projects',\n UPDATE_PROJECTS = 'update:projects',\n DELETE_PROJECTS = 'delete:projects',\n READ_TASKS = 'read:tasks',\n CREATE_TASKS = 'create:tasks',\n UPDATE_TASKS = 'update:tasks',\n DELETE_TASKS = 'delete:tasks',\n READ_TEAMS = 'read:teams',\n CREATE_TEAMS = 'create:teams',\n UPDATE_TEAMS = 'update:teams',\n DELETE_TEAMS = 'delete:teams',\n MANAGE_TEAM_MEMBERS = 'manage:team_members',\n READ_REPORTS = 'read:reports',\n CREATE_REPORTS = 'create:reports',\n USE_AI = 'use:ai',\n READ_PLANS = 'read:plans',\n CREATE_PLANS = 'create:plans',\n UPDATE_PLANS = 'update:plans',\n DELETE_PLANS = 'delete:plans',\n MANAGE_INTEGRATIONS = 'manage:integrations',\n MANAGE_AUTOMATIONS = 'manage:automations',\n VIEW_ANALYTICS = 'view:analytics',\n}\n\nexport const RolePermissions: Record<string, Permission[]> = {\n admin: [...Object.values(Permission)],\n manager: [\n Permission.READ_OWN_PROFILE,\n Permission.UPDATE_OWN_PROFILE,\n Permission.READ_PROJECTS,\n Permission.CREATE_PROJECTS,\n Permission.UPDATE_PROJECTS,\n Permission.READ_TASKS,\n Permission.CREATE_TASKS,\n Permission.UPDATE_TASKS,\n Permission.DELETE_TASKS,\n Permission.READ_TEAMS,\n Permission.CREATE_TEAMS,\n Permission.UPDATE_TEAMS,\n Permission.MANAGE_TEAM_MEMBERS,\n Permission.READ_REPORTS,\n Permission.CREATE_REPORTS,\n Permission.USE_AI,\n Permission.VIEW_ANALYTICS,\n ],\n user: [\n Permission.READ_OWN_PROFILE,\n Permission.UPDATE_OWN_PROFILE,\n Permission.READ_PROJECTS,\n Permission.CREATE_PROJECTS,\n Permission.UPDATE_PROJECTS,\n Permission.READ_TASKS,\n Permission.CREATE_TASKS,\n Permission.UPDATE_TASKS,\n Permission.DELETE_TASKS,\n Permission.READ_TEAMS,\n Permission.CREATE_TEAMS,\n Permission.READ_REPORTS,\n Permission.USE_AI,\n ],\n};\n","// ─────────────────────────────────────────────\n// @organify/auth-sdk — JWT verification helpers\n// ─────────────────────────────────────────────\n\nimport jwt from 'jsonwebtoken';\nimport type { JwtPayload as OrganifyJwtPayload } from './types';\n\nexport interface VerifyOptions {\n secret: string;\n /** When true, expired tokens won't throw */\n ignoreExpiration?: boolean;\n}\n\n/**\n * Verify and decode a JWT token.\n * @throws Error if token is invalid or expired\n */\nexport function verifyToken(\n token: string,\n options: VerifyOptions,\n): OrganifyJwtPayload {\n return jwt.verify(token, options.secret, {\n ignoreExpiration: options.ignoreExpiration,\n }) as unknown as OrganifyJwtPayload;\n}\n\n/**\n * Decode a JWT without verifying (useful for debugging).\n */\nexport function decodeToken(token: string): OrganifyJwtPayload | null {\n return jwt.decode(token) as OrganifyJwtPayload | null;\n}\n\n/**\n * Extract Bearer token from Authorization header.\n */\nexport function extractBearerToken(\n authHeader: string | undefined,\n): string | null {\n if (!authHeader?.startsWith('Bearer ')) return null;\n return authHeader.substring(7);\n}\n\n/**\n * Extract a token from a cookie header string.\n */\nexport function extractCookieToken(\n cookieHeader: string | undefined,\n cookieName = 'auth_token',\n): string | null {\n if (!cookieHeader) return null;\n const match = cookieHeader\n .split(';')\n .map((s) => s.trim())\n .find((s) => s.startsWith(`${cookieName}=`));\n if (!match) return null;\n return decodeURIComponent(match.split('=')[1]);\n}\n\n/**\n * Check if a user has a specific permission based on their role.\n */\nexport function hasPermission(\n role: string,\n permission: string,\n rolePermissions: Record<string, string[]>,\n): boolean {\n const perms = rolePermissions[role];\n if (!perms) return false;\n return perms.includes(permission);\n}\n\n/**\n * Validate a service-to-service token.\n */\nexport function validateServiceToken(\n token: string | undefined,\n expectedToken: string,\n): boolean {\n if (!token) return false;\n return token === expectedToken;\n}\n","// ─────────────────────────────────────────────\n// FBToken Client — Front-to-Back Token for API Requests\n// ─────────────────────────────────────────────\n// Computes HMAC-SHA512(secret, timeWindow) matching the gateway.\n// Every 30 minutes the token rotates.\n//\n// Usage in frontend:\n// import { getFBToken } from '@organify/auth-sdk/fb-token';\n// headers['X-FB-Token'] = getFBToken();\n//\n// Usage in Web Components:\n// Same import, same usage.\n//\n// The secret comes from an environment variable:\n// NEXT_PUBLIC_FB_TOKEN_SECRET (Next.js)\n// VITE_FB_TOKEN_SECRET (Vite)\n// ─────────────────────────────────────────────\n\n/** Duration of each time window in milliseconds (30 minutes) */\nconst WINDOW_MS = 30 * 60 * 1000;\n\n/** Cache: avoid recomputing on every request within same window */\nlet cachedToken: string | null = null;\nlet cachedWindowIndex = -1;\n\n/**\n * Compute the current time-window index (epoch / 30min).\n */\nfunction getTimeWindowIndex(): number {\n return Math.floor(Date.now() / WINDOW_MS);\n}\n\n/**\n * HMAC-SHA512 using Web Crypto API (works in browser + Edge + Node 18+).\n */\nasync function hmacSHA512(secret: string, message: string): Promise<string> {\n const encoder = new TextEncoder();\n const keyData = encoder.encode(secret);\n const msgData = encoder.encode(message);\n\n const key = await crypto.subtle.importKey(\n 'raw',\n keyData,\n { name: 'HMAC', hash: 'SHA-512' },\n false,\n ['sign'],\n );\n\n const signature = await crypto.subtle.sign('HMAC', key, msgData);\n const bytes = new Uint8Array(signature);\n\n // Convert to hex\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Get the current FBToken for API requests.\n * Call this on every request — it caches within the same 30-min window.\n *\n * @param secret - The shared FB_TOKEN_SECRET. If not provided,\n * reads from NEXT_PUBLIC_FB_TOKEN_SECRET or VITE_FB_TOKEN_SECRET.\n * @returns The HMAC-SHA512 hex string to send in X-FB-Token header.\n */\nexport async function getFBToken(secret?: string): Promise<string> {\n const windowIndex = getTimeWindowIndex();\n\n // Return cached token if still in same window\n if (cachedToken && cachedWindowIndex === windowIndex) {\n return cachedToken;\n }\n\n const resolvedSecret =\n secret ||\n (typeof process !== 'undefined' && (process.env as any)?.NEXT_PUBLIC_FB_TOKEN_SECRET) ||\n (typeof import.meta !== 'undefined' && (import.meta as any)?.env?.VITE_FB_TOKEN_SECRET) ||\n '';\n\n if (!resolvedSecret) {\n console.warn('[FBToken] No FB_TOKEN_SECRET configured');\n return '';\n }\n\n const token = await hmacSHA512(\n resolvedSecret,\n `organify-fb-v1:${windowIndex}`,\n );\n\n cachedToken = token;\n cachedWindowIndex = windowIndex;\n\n return token;\n}\n\n/**\n * Create an Axios/fetch interceptor that automatically adds X-FB-Token.\n *\n * Usage with fetch:\n * const headers = await createFBTokenHeaders();\n * fetch(url, { headers: { ...headers, ...otherHeaders } });\n *\n * Usage with Axios:\n * axios.interceptors.request.use(fbTokenAxiosInterceptor(secret));\n */\nexport async function createFBTokenHeaders(secret?: string): Promise<Record<string, string>> {\n const token = await getFBToken(secret);\n if (!token) return {};\n return { 'X-FB-Token': token };\n}\n\n/**\n * Axios request interceptor factory.\n */\nexport function fbTokenAxiosInterceptor(secret?: string) {\n return async (config: any) => {\n const token = await getFBToken(secret);\n if (token) {\n config.headers = config.headers || {};\n config.headers['X-FB-Token'] = token;\n }\n return config;\n };\n}\n","// ─────────────────────────────────────────────\n// ServiceClient — Inter-Service HTTP Client with MessagePack\n// ─────────────────────────────────────────────\n// All service-to-service communication uses MessagePack for\n// serialization/deserialization. This client:\n//\n// 1. Sends request bodies as MessagePack (Content-Type: application/x-msgpack)\n// 2. Requests MessagePack responses (Accept: application/x-msgpack)\n// 3. Injects X-Service-Token for authentication\n// 4. Automatically decodes MessagePack responses\n// 5. Falls back to JSON when MessagePack is not available\n//\n// Usage:\n// const client = new ServiceClient({\n// serviceToken: 'shared-secret',\n// });\n// const users = await client.get<User[]>('http://users:4001/api/internal/users/batch');\n// const workspace = await client.post('http://workspaces:4002/api/internal/workspaces/personal', { userId: '1', userName: 'John' });\n// ─────────────────────────────────────────────\n\nimport { encode, decode } from '@msgpack/msgpack';\n\nexport const MSGPACK_MIME = 'application/x-msgpack';\n\nexport interface ServiceClientConfig {\n /** Shared service-to-service token (SERVICE_TO_SERVICE_TOKEN) */\n serviceToken: string;\n /** Default timeout in ms (default: 10000) */\n timeout?: number;\n /** Additional default headers */\n headers?: Record<string, string>;\n}\n\nexport interface ServiceRequestOptions {\n /** Override timeout for this request */\n timeout?: number;\n /** Additional headers for this request */\n headers?: Record<string, string>;\n /** Override user ID to forward downstream */\n userId?: string;\n}\n\nexport interface ServiceResponse<T = any> {\n data: T;\n status: number;\n headers: Record<string, string>;\n}\n\nexport class ServiceClient {\n private config: Required<Pick<ServiceClientConfig, 'serviceToken' | 'timeout'>> & {\n headers: Record<string, string>;\n };\n\n constructor(config: ServiceClientConfig) {\n this.config = {\n serviceToken: config.serviceToken,\n timeout: config.timeout ?? 10_000,\n headers: config.headers ?? {},\n };\n }\n\n // ─── HTTP Methods ─────────────────────────\n\n async get<T = any>(url: string, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>> {\n return this.request<T>('GET', url, undefined, opts);\n }\n\n async post<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>> {\n return this.request<T>('POST', url, body, opts);\n }\n\n async put<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>> {\n return this.request<T>('PUT', url, body, opts);\n }\n\n async patch<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>> {\n return this.request<T>('PATCH', url, body, opts);\n }\n\n async delete<T = any>(url: string, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>> {\n return this.request<T>('DELETE', url, undefined, opts);\n }\n\n // ─── Core Request ─────────────────────────\n\n private async request<T>(\n method: string,\n url: string,\n body?: any,\n opts?: ServiceRequestOptions,\n ): Promise<ServiceResponse<T>> {\n const timeout = opts?.timeout ?? this.config.timeout;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeout);\n\n try {\n const headers: Record<string, string> = {\n // Always use MessagePack for internal communication\n 'Accept': MSGPACK_MIME,\n 'X-Service-Token': this.config.serviceToken,\n ...this.config.headers,\n ...opts?.headers,\n };\n\n // Forward user identity if provided\n if (opts?.userId) {\n headers['x-user-id'] = opts.userId;\n }\n\n const fetchOpts: RequestInit = {\n method,\n headers,\n signal: controller.signal,\n };\n\n // Encode body as MessagePack if present\n if (body !== undefined && body !== null) {\n const packed = encode(body);\n fetchOpts.body = Buffer.from(packed.buffer, packed.byteOffset, packed.byteLength);\n headers['Content-Type'] = MSGPACK_MIME;\n }\n\n const response = await fetch(url, fetchOpts);\n\n // Parse response based on Content-Type\n const contentType = response.headers.get('content-type') || '';\n let data: T;\n\n if (/msgpack/i.test(contentType)) {\n const buffer = await response.arrayBuffer();\n data = decode(new Uint8Array(buffer)) as T;\n } else {\n // Fallback to JSON\n const text = await response.text();\n try {\n data = JSON.parse(text) as T;\n } catch {\n data = text as unknown as T;\n }\n }\n\n // Collect response headers\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n if (!response.ok) {\n throw new ServiceClientError(\n `Service request failed: ${method} ${url} → ${response.status}`,\n response.status,\n data,\n );\n }\n\n return { data, status: response.status, headers: responseHeaders };\n } finally {\n clearTimeout(timer);\n }\n }\n}\n\n// ─── Error Class ────────────────────────────\n\nexport class ServiceClientError extends Error {\n constructor(\n message: string,\n public readonly status: number,\n public readonly response: any,\n ) {\n super(message);\n this.name = 'ServiceClientError';\n }\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
interface JwtPayload {
|
|
2
|
+
/** User ID (subject) */
|
|
3
|
+
sub: number;
|
|
4
|
+
email: string;
|
|
5
|
+
name: string;
|
|
6
|
+
role: 'admin' | 'user' | 'manager';
|
|
7
|
+
plan: string;
|
|
8
|
+
teamIds: number[];
|
|
9
|
+
/** Token type — only present on refresh tokens */
|
|
10
|
+
tokenType?: 'refresh';
|
|
11
|
+
iat?: number;
|
|
12
|
+
exp?: number;
|
|
13
|
+
}
|
|
14
|
+
interface AuthenticatedUser {
|
|
15
|
+
userId: number;
|
|
16
|
+
email: string;
|
|
17
|
+
name: string;
|
|
18
|
+
role: string;
|
|
19
|
+
plan: string;
|
|
20
|
+
}
|
|
21
|
+
interface JwtTokens {
|
|
22
|
+
accessToken: string;
|
|
23
|
+
refreshToken: string;
|
|
24
|
+
}
|
|
25
|
+
declare enum Permission {
|
|
26
|
+
READ_OWN_PROFILE = "read:own_profile",
|
|
27
|
+
UPDATE_OWN_PROFILE = "update:own_profile",
|
|
28
|
+
READ_USERS = "read:users",
|
|
29
|
+
CREATE_USERS = "create:users",
|
|
30
|
+
UPDATE_USERS = "update:users",
|
|
31
|
+
DELETE_USERS = "delete:users",
|
|
32
|
+
READ_PROJECTS = "read:projects",
|
|
33
|
+
CREATE_PROJECTS = "create:projects",
|
|
34
|
+
UPDATE_PROJECTS = "update:projects",
|
|
35
|
+
DELETE_PROJECTS = "delete:projects",
|
|
36
|
+
READ_TASKS = "read:tasks",
|
|
37
|
+
CREATE_TASKS = "create:tasks",
|
|
38
|
+
UPDATE_TASKS = "update:tasks",
|
|
39
|
+
DELETE_TASKS = "delete:tasks",
|
|
40
|
+
READ_TEAMS = "read:teams",
|
|
41
|
+
CREATE_TEAMS = "create:teams",
|
|
42
|
+
UPDATE_TEAMS = "update:teams",
|
|
43
|
+
DELETE_TEAMS = "delete:teams",
|
|
44
|
+
MANAGE_TEAM_MEMBERS = "manage:team_members",
|
|
45
|
+
READ_REPORTS = "read:reports",
|
|
46
|
+
CREATE_REPORTS = "create:reports",
|
|
47
|
+
USE_AI = "use:ai",
|
|
48
|
+
READ_PLANS = "read:plans",
|
|
49
|
+
CREATE_PLANS = "create:plans",
|
|
50
|
+
UPDATE_PLANS = "update:plans",
|
|
51
|
+
DELETE_PLANS = "delete:plans",
|
|
52
|
+
MANAGE_INTEGRATIONS = "manage:integrations",
|
|
53
|
+
MANAGE_AUTOMATIONS = "manage:automations",
|
|
54
|
+
VIEW_ANALYTICS = "view:analytics"
|
|
55
|
+
}
|
|
56
|
+
declare const RolePermissions: Record<string, Permission[]>;
|
|
57
|
+
|
|
58
|
+
interface VerifyOptions {
|
|
59
|
+
secret: string;
|
|
60
|
+
/** When true, expired tokens won't throw */
|
|
61
|
+
ignoreExpiration?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Verify and decode a JWT token.
|
|
65
|
+
* @throws Error if token is invalid or expired
|
|
66
|
+
*/
|
|
67
|
+
declare function verifyToken(token: string, options: VerifyOptions): JwtPayload;
|
|
68
|
+
/**
|
|
69
|
+
* Decode a JWT without verifying (useful for debugging).
|
|
70
|
+
*/
|
|
71
|
+
declare function decodeToken(token: string): JwtPayload | null;
|
|
72
|
+
/**
|
|
73
|
+
* Extract Bearer token from Authorization header.
|
|
74
|
+
*/
|
|
75
|
+
declare function extractBearerToken(authHeader: string | undefined): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Extract a token from a cookie header string.
|
|
78
|
+
*/
|
|
79
|
+
declare function extractCookieToken(cookieHeader: string | undefined, cookieName?: string): string | null;
|
|
80
|
+
/**
|
|
81
|
+
* Check if a user has a specific permission based on their role.
|
|
82
|
+
*/
|
|
83
|
+
declare function hasPermission(role: string, permission: string, rolePermissions: Record<string, string[]>): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Validate a service-to-service token.
|
|
86
|
+
*/
|
|
87
|
+
declare function validateServiceToken(token: string | undefined, expectedToken: string): boolean;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get the current FBToken for API requests.
|
|
91
|
+
* Call this on every request — it caches within the same 30-min window.
|
|
92
|
+
*
|
|
93
|
+
* @param secret - The shared FB_TOKEN_SECRET. If not provided,
|
|
94
|
+
* reads from NEXT_PUBLIC_FB_TOKEN_SECRET or VITE_FB_TOKEN_SECRET.
|
|
95
|
+
* @returns The HMAC-SHA512 hex string to send in X-FB-Token header.
|
|
96
|
+
*/
|
|
97
|
+
declare function getFBToken(secret?: string): Promise<string>;
|
|
98
|
+
/**
|
|
99
|
+
* Create an Axios/fetch interceptor that automatically adds X-FB-Token.
|
|
100
|
+
*
|
|
101
|
+
* Usage with fetch:
|
|
102
|
+
* const headers = await createFBTokenHeaders();
|
|
103
|
+
* fetch(url, { headers: { ...headers, ...otherHeaders } });
|
|
104
|
+
*
|
|
105
|
+
* Usage with Axios:
|
|
106
|
+
* axios.interceptors.request.use(fbTokenAxiosInterceptor(secret));
|
|
107
|
+
*/
|
|
108
|
+
declare function createFBTokenHeaders(secret?: string): Promise<Record<string, string>>;
|
|
109
|
+
/**
|
|
110
|
+
* Axios request interceptor factory.
|
|
111
|
+
*/
|
|
112
|
+
declare function fbTokenAxiosInterceptor(secret?: string): (config: any) => Promise<any>;
|
|
113
|
+
|
|
114
|
+
declare const MSGPACK_MIME = "application/x-msgpack";
|
|
115
|
+
interface ServiceClientConfig {
|
|
116
|
+
/** Shared service-to-service token (SERVICE_TO_SERVICE_TOKEN) */
|
|
117
|
+
serviceToken: string;
|
|
118
|
+
/** Default timeout in ms (default: 10000) */
|
|
119
|
+
timeout?: number;
|
|
120
|
+
/** Additional default headers */
|
|
121
|
+
headers?: Record<string, string>;
|
|
122
|
+
}
|
|
123
|
+
interface ServiceRequestOptions {
|
|
124
|
+
/** Override timeout for this request */
|
|
125
|
+
timeout?: number;
|
|
126
|
+
/** Additional headers for this request */
|
|
127
|
+
headers?: Record<string, string>;
|
|
128
|
+
/** Override user ID to forward downstream */
|
|
129
|
+
userId?: string;
|
|
130
|
+
}
|
|
131
|
+
interface ServiceResponse<T = any> {
|
|
132
|
+
data: T;
|
|
133
|
+
status: number;
|
|
134
|
+
headers: Record<string, string>;
|
|
135
|
+
}
|
|
136
|
+
declare class ServiceClient {
|
|
137
|
+
private config;
|
|
138
|
+
constructor(config: ServiceClientConfig);
|
|
139
|
+
get<T = any>(url: string, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>>;
|
|
140
|
+
post<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>>;
|
|
141
|
+
put<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>>;
|
|
142
|
+
patch<T = any>(url: string, body?: any, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>>;
|
|
143
|
+
delete<T = any>(url: string, opts?: ServiceRequestOptions): Promise<ServiceResponse<T>>;
|
|
144
|
+
private request;
|
|
145
|
+
}
|
|
146
|
+
declare class ServiceClientError extends Error {
|
|
147
|
+
readonly status: number;
|
|
148
|
+
readonly response: any;
|
|
149
|
+
constructor(message: string, status: number, response: any);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export { type AuthenticatedUser, type JwtPayload, type JwtTokens, MSGPACK_MIME, Permission, RolePermissions, ServiceClient, type ServiceClientConfig, ServiceClientError, type ServiceRequestOptions, type ServiceResponse, type VerifyOptions, createFBTokenHeaders, decodeToken, extractBearerToken, extractCookieToken, fbTokenAxiosInterceptor, getFBToken, hasPermission, validateServiceToken, verifyToken };
|