@teamvortexsoftware/vortex-node-22-sdk 0.0.7 โ 0.0.8
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/README.md +47 -0
- package/dist/index.d.mts +31 -10
- package/dist/index.d.ts +31 -10
- package/dist/index.js +29 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,53 @@ Let's assume you have an express powered API and a user that is looking to invit
|
|
|
27
27
|
|
|
28
28
|
You could populate the JWT in some set of initial data that your application already provides (recommended) or you could create an endpoint specifically to fetch the JWT on demand for use with the widget. For the purposes of this example, we'll create an endpoint to fetch the JWT.
|
|
29
29
|
|
|
30
|
+
#### Current Format (Recommended)
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const express = require('express');
|
|
34
|
+
const app = express();
|
|
35
|
+
const port = 3000;
|
|
36
|
+
|
|
37
|
+
// Provide your API key however you see fit.
|
|
38
|
+
const vortex = new Vortex(process.env.VORTEX_API_KEY);
|
|
39
|
+
|
|
40
|
+
app.get('/vortex-jwt', (req, res) => {
|
|
41
|
+
res.setHeader('Content-Type', 'application/json');
|
|
42
|
+
|
|
43
|
+
const token = vortex.generateJwt({
|
|
44
|
+
user: {
|
|
45
|
+
id: "user-123",
|
|
46
|
+
email: "user@example.com",
|
|
47
|
+
adminScopes: ['autoJoin'] // Optional: grants admin privileges for auto-joining
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
res.end(JSON.stringify({ jwt: token }));
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
app.listen(port, () => {
|
|
55
|
+
console.log(`Example app listening on port ${port}. Fetch example JWT by hitting /vortex-jwt`)
|
|
56
|
+
})
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also add extra properties to the JWT payload:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const token = vortex.generateJwt({
|
|
63
|
+
user: {
|
|
64
|
+
id: "user-123",
|
|
65
|
+
email: "user@example.com",
|
|
66
|
+
adminScopes: ['autoJoin']
|
|
67
|
+
},
|
|
68
|
+
role: "admin",
|
|
69
|
+
department: "Engineering"
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Legacy Format (Deprecated)
|
|
74
|
+
|
|
75
|
+
The legacy format is still supported for backward compatibility but is deprecated. New integrations should use the simplified format above.
|
|
76
|
+
|
|
30
77
|
```ts
|
|
31
78
|
const express = require('express');
|
|
32
79
|
const app = express();
|
package/dist/index.d.mts
CHANGED
|
@@ -60,19 +60,40 @@ type ApiResponseJson = InvitationResult | {
|
|
|
60
60
|
invitations: InvitationResult[];
|
|
61
61
|
} | {};
|
|
62
62
|
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
63
|
+
/**
|
|
64
|
+
* User type for JWT generation
|
|
65
|
+
*/
|
|
66
|
+
type User = {
|
|
67
|
+
id: string;
|
|
68
|
+
email: string;
|
|
69
|
+
adminScopes?: string[];
|
|
70
|
+
[key: string]: any;
|
|
71
|
+
};
|
|
63
72
|
|
|
64
73
|
declare class Vortex {
|
|
65
74
|
private apiKey;
|
|
66
75
|
constructor(apiKey: string);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Generate a JWT token for a user
|
|
78
|
+
*
|
|
79
|
+
* @param params - Object containing user and optional additional properties
|
|
80
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
81
|
+
* @returns JWT token string
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const token = vortex.generateJwt({
|
|
86
|
+
* user: {
|
|
87
|
+
* id: "user-123",
|
|
88
|
+
* email: "user@example.com",
|
|
89
|
+
* adminScopes: ['autoJoin']
|
|
90
|
+
* }
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
generateJwt(params: {
|
|
95
|
+
user: User;
|
|
96
|
+
[key: string]: any;
|
|
76
97
|
}): string;
|
|
77
98
|
vortexApiRequest(options: {
|
|
78
99
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -92,4 +113,4 @@ declare class Vortex {
|
|
|
92
113
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
93
114
|
}
|
|
94
115
|
|
|
95
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, Vortex };
|
|
116
|
+
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
package/dist/index.d.ts
CHANGED
|
@@ -60,19 +60,40 @@ type ApiResponseJson = InvitationResult | {
|
|
|
60
60
|
invitations: InvitationResult[];
|
|
61
61
|
} | {};
|
|
62
62
|
type ApiRequestBody = AcceptInvitationRequest | null;
|
|
63
|
+
/**
|
|
64
|
+
* User type for JWT generation
|
|
65
|
+
*/
|
|
66
|
+
type User = {
|
|
67
|
+
id: string;
|
|
68
|
+
email: string;
|
|
69
|
+
adminScopes?: string[];
|
|
70
|
+
[key: string]: any;
|
|
71
|
+
};
|
|
63
72
|
|
|
64
73
|
declare class Vortex {
|
|
65
74
|
private apiKey;
|
|
66
75
|
constructor(apiKey: string);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Generate a JWT token for a user
|
|
78
|
+
*
|
|
79
|
+
* @param params - Object containing user and optional additional properties
|
|
80
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
81
|
+
* @returns JWT token string
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* const token = vortex.generateJwt({
|
|
86
|
+
* user: {
|
|
87
|
+
* id: "user-123",
|
|
88
|
+
* email: "user@example.com",
|
|
89
|
+
* adminScopes: ['autoJoin']
|
|
90
|
+
* }
|
|
91
|
+
* });
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
generateJwt(params: {
|
|
95
|
+
user: User;
|
|
96
|
+
[key: string]: any;
|
|
76
97
|
}): string;
|
|
77
98
|
vortexApiRequest(options: {
|
|
78
99
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
@@ -92,4 +113,4 @@ declare class Vortex {
|
|
|
92
113
|
reinvite(invitationId: string): Promise<InvitationResult>;
|
|
93
114
|
}
|
|
94
115
|
|
|
95
|
-
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, Vortex };
|
|
116
|
+
export { type AcceptInvitationRequest, type ApiRequestBody, type ApiResponseJson, type GroupInput, type InvitationAcceptance, type InvitationGroup, type InvitationResult, type InvitationTarget, type User, Vortex };
|
package/dist/index.js
CHANGED
|
@@ -41,13 +41,26 @@ var Vortex = class {
|
|
|
41
41
|
constructor(apiKey) {
|
|
42
42
|
this.apiKey = apiKey;
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Generate a JWT token for a user
|
|
46
|
+
*
|
|
47
|
+
* @param params - Object containing user and optional additional properties
|
|
48
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
49
|
+
* @returns JWT token string
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const token = vortex.generateJwt({
|
|
54
|
+
* user: {
|
|
55
|
+
* id: "user-123",
|
|
56
|
+
* email: "user@example.com",
|
|
57
|
+
* adminScopes: ['autoJoin']
|
|
58
|
+
* }
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
generateJwt(params) {
|
|
63
|
+
const { user, ...rest } = params;
|
|
51
64
|
const [prefix, encodedId, key] = this.apiKey.split(".");
|
|
52
65
|
if (!prefix || !encodedId || !key) {
|
|
53
66
|
throw new Error("Invalid API key format");
|
|
@@ -65,13 +78,16 @@ var Vortex = class {
|
|
|
65
78
|
kid: id
|
|
66
79
|
};
|
|
67
80
|
const payload = {
|
|
68
|
-
userId,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
expires,
|
|
72
|
-
identifiers,
|
|
73
|
-
...attributes && Object.keys(attributes).length > 0 ? { attributes } : {}
|
|
81
|
+
userId: user.id,
|
|
82
|
+
userEmail: user.email,
|
|
83
|
+
expires
|
|
74
84
|
};
|
|
85
|
+
if (user.adminScopes) {
|
|
86
|
+
payload.adminScopes = user.adminScopes;
|
|
87
|
+
}
|
|
88
|
+
if (rest && Object.keys(rest).length > 0) {
|
|
89
|
+
Object.assign(payload, rest);
|
|
90
|
+
}
|
|
75
91
|
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
76
92
|
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
77
93
|
const toSign = `${headerB64}.${payloadB64}`;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/vortex.ts"],"sourcesContent":["export * from './vortex';\nexport * from './types';","import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest, User } from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) { }\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autoJoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body?: ApiRequestBody,\n queryParams?: Record<string, string | number | boolean>,\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(`${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`);\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations?targetType',\n queryParams: {\n targetType,\n targetValue,\n }\n }) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n }) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,yBAAmB;AACnB,kBAA2C;AAGpC,IAAM,SAAN,MAAa;AAAA,EAClB,YAAoB,QAAgB;AAAhB;AAAA,EAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBtC,YAAY,QAAoD;AAC9D,UAAM,EAAE,MAAM,GAAG,KAAK,IAAI;AAC1B,UAAM,CAAC,QAAQ,WAAW,GAAG,IAAI,KAAK,OAAO,MAAM,GAAG;AACtD,QAAI,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK;AACjC,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,QAAI,WAAW,QAAQ;AACrB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AACA,UAAM,SAAK,YAAAA,WAAc,OAAO,KAAK,WAAW,WAAW,CAAC;AAE5D,UAAM,UAAU,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,IAAI;AAGhD,UAAM,aAAa,mBAAAC,QAAO,WAAW,UAAU,GAAG,EAAE,OAAO,EAAE,EAAE,OAAO;AAGtE,UAAM,SAAS;AAAA,MACb,KAAK,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,MACjC,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAGA,UAAM,UAAe;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,WAAW,KAAK;AAAA,MAChB;AAAA,IACF;AAGA,QAAI,KAAK,aAAa;AACpB,cAAQ,cAAc,KAAK;AAAA,IAC7B;AAGA,QAAI,QAAQ,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACxC,aAAO,OAAO,SAAS,IAAI;AAAA,IAC7B;AAGA,UAAM,YAAY,OAAO,KAAK,KAAK,UAAU,MAAM,CAAC,EAAE,SAAS,WAAW;AAC1E,UAAM,aAAa,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,EAAE,SAAS,WAAW;AAG5E,UAAM,SAAS,GAAG,SAAS,IAAI,UAAU;AACzC,UAAM,YAAY,OAAO;AAAA,MACvB,mBAAAA,QAAO,WAAW,UAAU,UAAU,EAAE,OAAO,MAAM,EAAE,OAAO;AAAA,IAChE,EAAE,SAAS,WAAW;AACtB,UAAM,MAAM,GAAG,MAAM,IAAI,SAAS;AAClC,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,iBAAiB,SAKM;AAC3B,UAAM,EAAE,QAAQ,MAAM,MAAM,YAAY,IAAI;AAC5C,UAAM,MAAM,IAAI,IAAI,GAAG,QAAQ,IAAI,uBAAuB,gCAAgC,GAAG,IAAI,EAAE;AACnG,QAAI,aAAa;AACf,aAAO,QAAQ,WAAW,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACpD,YAAI,aAAa,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,MAC5C,CAAC;AAAA,IACH;AACA,UAAM,UAAU,MAAM,MAAM,IAAI,SAAS,GAAG;AAAA,MAC1C;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AACD,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,YAAY,MAAM,QAAQ,KAAK;AACrC,YAAM,IAAI,MAAM,8BAA8B,QAAQ,MAAM,IAAI,QAAQ,UAAU,MAAM,SAAS,EAAE;AAAA,IACrG;AAGA,UAAM,gBAAgB,QAAQ,QAAQ,IAAI,gBAAgB;AAC1D,UAAM,cAAc,QAAQ,QAAQ,IAAI,cAAc;AAGtD,QAAI,kBAAkB,OAAQ,EAAC,2CAAa,SAAS,wBAAuB,CAAC,eAAgB;AAC3F,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,eAAe,MAAM,QAAQ,KAAK;AACxC,QAAI,CAAC,aAAa,KAAK,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAGA,QAAI;AACF,aAAO,KAAK,MAAM,YAAY;AAAA,IAChC,SAAS,OAAO;AAEd,aAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,uBAAuB,YAAkD,aAAkD;AAC/H,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAAA,MAC3C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,aAAa;AAAA,QACX;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,cAAc,cAAiD;AACnE,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,iBAAiB,cAAmC;AACxD,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,kBACJ,eACA,QAC2B;AAC3B,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAAA,MAC3C,QAAQ;AAAA,MACR,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,MACF;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,yBAAyB,WAAmB,SAA8B;AAC9E,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,gCAAgC,SAAS,IAAI,OAAO;AAAA,IAC5D,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,sBAAsB,WAAmB,SAA8C;AAC3F,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAAA,MAC3C,QAAQ;AAAA,MACR,MAAM,gCAAgC,SAAS,IAAI,OAAO;AAAA,IAC5D,CAAC;AACD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,MAAM,SAAS,cAAiD;AAC9D,WAAO,KAAK,iBAAiB;AAAA,MAC3B,QAAQ;AAAA,MACR,MAAM,uBAAuB,YAAY;AAAA,IAC3C,CAAC;AAAA,EACH;AACF;","names":["uuidStringify","crypto"]}
|
package/dist/index.mjs
CHANGED
|
@@ -5,13 +5,26 @@ var Vortex = class {
|
|
|
5
5
|
constructor(apiKey) {
|
|
6
6
|
this.apiKey = apiKey;
|
|
7
7
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Generate a JWT token for a user
|
|
10
|
+
*
|
|
11
|
+
* @param params - Object containing user and optional additional properties
|
|
12
|
+
* @param params.user - User object with id, email, and optional adminScopes
|
|
13
|
+
* @returns JWT token string
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const token = vortex.generateJwt({
|
|
18
|
+
* user: {
|
|
19
|
+
* id: "user-123",
|
|
20
|
+
* email: "user@example.com",
|
|
21
|
+
* adminScopes: ['autoJoin']
|
|
22
|
+
* }
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
generateJwt(params) {
|
|
27
|
+
const { user, ...rest } = params;
|
|
15
28
|
const [prefix, encodedId, key] = this.apiKey.split(".");
|
|
16
29
|
if (!prefix || !encodedId || !key) {
|
|
17
30
|
throw new Error("Invalid API key format");
|
|
@@ -29,13 +42,16 @@ var Vortex = class {
|
|
|
29
42
|
kid: id
|
|
30
43
|
};
|
|
31
44
|
const payload = {
|
|
32
|
-
userId,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expires,
|
|
36
|
-
identifiers,
|
|
37
|
-
...attributes && Object.keys(attributes).length > 0 ? { attributes } : {}
|
|
45
|
+
userId: user.id,
|
|
46
|
+
userEmail: user.email,
|
|
47
|
+
expires
|
|
38
48
|
};
|
|
49
|
+
if (user.adminScopes) {
|
|
50
|
+
payload.adminScopes = user.adminScopes;
|
|
51
|
+
}
|
|
52
|
+
if (rest && Object.keys(rest).length > 0) {
|
|
53
|
+
Object.assign(payload, rest);
|
|
54
|
+
}
|
|
39
55
|
const headerB64 = Buffer.from(JSON.stringify(header)).toString("base64url");
|
|
40
56
|
const payloadB64 = Buffer.from(JSON.stringify(payload)).toString("base64url");
|
|
41
57
|
const toSign = `${headerB64}.${payloadB64}`;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest,
|
|
1
|
+
{"version":3,"sources":["../src/vortex.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { stringify as uuidStringify } from 'uuid';\nimport { ApiRequestBody, ApiResponseJson, InvitationResult, AcceptInvitationRequest, User } from './types';\n\nexport class Vortex {\n constructor(private apiKey: string) { }\n\n /**\n * Generate a JWT token for a user\n *\n * @param params - Object containing user and optional additional properties\n * @param params.user - User object with id, email, and optional adminScopes\n * @returns JWT token string\n *\n * @example\n * ```typescript\n * const token = vortex.generateJwt({\n * user: {\n * id: \"user-123\",\n * email: \"user@example.com\",\n * adminScopes: ['autoJoin']\n * }\n * });\n * ```\n */\n generateJwt(params: { user: User; [key: string]: any }): string {\n const { user, ...rest } = params;\n const [prefix, encodedId, key] = this.apiKey.split('.'); // prefix is just VRTX\n if (!prefix || !encodedId || !key) {\n throw new Error('Invalid API key format');\n }\n if (prefix !== 'VRTX') {\n throw new Error('Invalid API key prefix');\n }\n const id = uuidStringify(Buffer.from(encodedId, 'base64url'));\n\n const expires = Math.floor(Date.now() / 1000) + 3600;\n\n // ๐ Step 1: Derive signing key from API key + ID\n const signingKey = crypto.createHmac('sha256', key).update(id).digest(); // <- raw Buffer\n\n // ๐งฑ Step 2: Build header + payload\n const header = {\n iat: Math.floor(Date.now() / 1000),\n alg: 'HS256',\n typ: 'JWT',\n kid: id,\n };\n\n // Build payload with user data\n const payload: any = {\n userId: user.id,\n userEmail: user.email,\n expires,\n };\n\n // Add adminScopes if present\n if (user.adminScopes) {\n payload.adminScopes = user.adminScopes;\n }\n\n // Add any additional properties from rest\n if (rest && Object.keys(rest).length > 0) {\n Object.assign(payload, rest);\n }\n\n // ๐งฑ Step 3: Base64URL encode\n const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');\n const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');\n\n // ๐งพ Step 4: Sign\n const toSign = `${headerB64}.${payloadB64}`;\n const signature = Buffer.from(\n crypto.createHmac('sha256', signingKey).update(toSign).digest()\n ).toString('base64url');\n const jwt = `${toSign}.${signature}`;\n return jwt;\n }\n\n async vortexApiRequest(options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n path: string,\n body?: ApiRequestBody,\n queryParams?: Record<string, string | number | boolean>,\n }): Promise<ApiResponseJson> {\n const { method, path, body, queryParams } = options;\n const url = new URL(`${process.env.VORTEX_API_BASE_URL || 'https://api.vortexsoftware.com'}${path}`);\n if (queryParams) {\n Object.entries(queryParams).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n const results = await fetch(url.toString(), {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!results.ok) {\n const errorBody = await results.text();\n throw new Error(`Vortex API request failed: ${results.status} ${results.statusText} - ${errorBody}`);\n }\n\n // Check if response has content to parse\n const contentLength = results.headers.get('content-length');\n const contentType = results.headers.get('content-type');\n\n // If no content or content-length is 0, return empty object\n if (contentLength === '0' || (!contentType?.includes('application/json') && !contentLength)) {\n return {};\n }\n\n // Try to get text first to check if there's actually content\n const responseText = await results.text();\n if (!responseText.trim()) {\n return {};\n }\n\n // Parse JSON if there's content\n try {\n return JSON.parse(responseText);\n } catch (error) {\n // If JSON parsing fails, return the text or empty object\n return {};\n }\n }\n\n async getInvitationsByTarget(targetType: 'email' | 'username' | 'phoneNumber', targetValue: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: '/api/v1/invitations?targetType',\n queryParams: {\n targetType,\n targetValue,\n }\n }) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async getInvitation(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<InvitationResult>;\n }\n\n async revokeInvitation(invitationId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/${invitationId}`,\n }) as Promise<{}>;\n }\n\n async acceptInvitations(\n invitationIds: string[],\n target: { type: 'email' | 'username' | 'phoneNumber'; value: string }\n ): Promise<InvitationResult> {\n const response = await this.vortexApiRequest({\n method: 'POST',\n body: {\n invitationIds,\n target,\n } as AcceptInvitationRequest,\n path: `/api/v1/invitations/accept`,\n }) as InvitationResult;\n return response;\n }\n\n async deleteInvitationsByGroup(groupType: string, groupId: string): Promise<{}> {\n return this.vortexApiRequest({\n method: 'DELETE',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as Promise<{}>;\n }\n\n async getInvitationsByGroup(groupType: string, groupId: string): Promise<InvitationResult[]> {\n const response = await this.vortexApiRequest({\n method: 'GET',\n path: `/api/v1/invitations/by-group/${groupType}/${groupId}`,\n }) as { invitations: InvitationResult[] };\n return response.invitations;\n }\n\n async reinvite(invitationId: string): Promise<InvitationResult> {\n return this.vortexApiRequest({\n method: 'POST',\n path: `/api/v1/invitations/${invitationId}/reinvite`,\n }) as Promise<InvitationResult>;\n }\n}"],"mappings":"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","names":[]}
|
package/package.json
CHANGED