@banata-auth/sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.cjs +1281 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1095 -0
- package/dist/index.d.ts +1095 -0
- package/dist/index.js +1234 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1234 @@
|
|
|
1
|
+
import { createErrorFromStatus } from '@banata-auth/shared';
|
|
2
|
+
export { AuthenticationError, BanataAuthError, ConflictError, ForbiddenError, InternalError, NotFoundError, RateLimitError, ValidationError } from '@banata-auth/shared';
|
|
3
|
+
|
|
4
|
+
// src/client.ts
|
|
5
|
+
|
|
6
|
+
// src/resources/api-keys.ts
|
|
7
|
+
var ApiKeys = class {
|
|
8
|
+
constructor(http) {
|
|
9
|
+
this.http = http;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create a new API key.
|
|
13
|
+
*
|
|
14
|
+
* @returns The created key metadata **plus** the raw `key` value.
|
|
15
|
+
* The raw key is only returned at creation time and cannot be retrieved later.
|
|
16
|
+
*/
|
|
17
|
+
async create(options) {
|
|
18
|
+
return this.http.post("/api/auth/api-key/create", {
|
|
19
|
+
name: options.name,
|
|
20
|
+
organizationId: options.organizationId,
|
|
21
|
+
permissions: options.permissions,
|
|
22
|
+
expiresAt: options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* List all API keys. The raw key value is **not** included.
|
|
27
|
+
*/
|
|
28
|
+
async list() {
|
|
29
|
+
return this.http.get("/api/auth/api-key/list");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Delete (revoke) an API key by its ID.
|
|
33
|
+
*/
|
|
34
|
+
async delete(keyId) {
|
|
35
|
+
return this.http.post("/api/auth/api-key/delete", { keyId });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// src/resources/audit-logs.ts
|
|
40
|
+
var AuditLogs = class {
|
|
41
|
+
constructor(http) {
|
|
42
|
+
this.http = http;
|
|
43
|
+
}
|
|
44
|
+
async createEvent(options) {
|
|
45
|
+
return this.http.post("/api/auth/banata/audit-logs/create", {
|
|
46
|
+
action: options.action,
|
|
47
|
+
actorType: options.actorType,
|
|
48
|
+
actorId: options.actorId,
|
|
49
|
+
actorName: options.actorName,
|
|
50
|
+
actorEmail: options.actorEmail,
|
|
51
|
+
targets: options.targets ? JSON.stringify(options.targets) : void 0,
|
|
52
|
+
organizationId: options.organizationId,
|
|
53
|
+
ipAddress: options.ipAddress,
|
|
54
|
+
userAgent: options.userAgent,
|
|
55
|
+
changes: options.changes ? JSON.stringify(options.changes) : void 0,
|
|
56
|
+
idempotencyKey: options.idempotencyKey,
|
|
57
|
+
metadata: options.metadata ? JSON.stringify(options.metadata) : void 0,
|
|
58
|
+
occurredAt: options.occurredAt?.getTime()
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async listEvents(options) {
|
|
62
|
+
return this.http.post("/api/auth/banata/audit-logs/list", {
|
|
63
|
+
organizationId: options?.organizationId,
|
|
64
|
+
action: options?.action,
|
|
65
|
+
actorId: options?.actorId,
|
|
66
|
+
after: options?.after,
|
|
67
|
+
before: options?.before,
|
|
68
|
+
limit: options?.limit
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async exportEvents(options) {
|
|
72
|
+
return this.http.post("/api/auth/banata/audit-logs/export", {
|
|
73
|
+
organizationId: options.organizationId,
|
|
74
|
+
format: options.format,
|
|
75
|
+
startDate: options.startDate?.getTime(),
|
|
76
|
+
endDate: options.endDate?.getTime(),
|
|
77
|
+
action: options.action,
|
|
78
|
+
limit: options.limit,
|
|
79
|
+
cursor: options.cursor
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/resources/directory-sync.ts
|
|
85
|
+
var DirectorySync = class {
|
|
86
|
+
constructor(http) {
|
|
87
|
+
this.http = http;
|
|
88
|
+
}
|
|
89
|
+
async listDirectories(options) {
|
|
90
|
+
return this.http.post(
|
|
91
|
+
"/api/auth/banata/scim/list-providers",
|
|
92
|
+
this.http.withProjectScope(
|
|
93
|
+
{
|
|
94
|
+
organizationId: options?.organizationId,
|
|
95
|
+
limit: options?.limit,
|
|
96
|
+
before: options?.before,
|
|
97
|
+
after: options?.after
|
|
98
|
+
},
|
|
99
|
+
options?.projectId
|
|
100
|
+
)
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
async getDirectory(directoryId, options) {
|
|
104
|
+
return this.http.post(
|
|
105
|
+
"/api/auth/banata/scim/get-provider",
|
|
106
|
+
this.http.withProjectScope(
|
|
107
|
+
{
|
|
108
|
+
providerId: directoryId
|
|
109
|
+
},
|
|
110
|
+
options?.projectId
|
|
111
|
+
)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
async createDirectory(options) {
|
|
115
|
+
return this.http.post(
|
|
116
|
+
"/api/auth/banata/scim/register",
|
|
117
|
+
this.http.withProjectScope(
|
|
118
|
+
{
|
|
119
|
+
organizationId: options.organizationId,
|
|
120
|
+
name: options.name,
|
|
121
|
+
provider: options.provider
|
|
122
|
+
},
|
|
123
|
+
options.projectId
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
async deleteDirectory(directoryId, options) {
|
|
128
|
+
return this.http.post(
|
|
129
|
+
"/api/auth/banata/scim/delete-provider",
|
|
130
|
+
this.http.withProjectScope(
|
|
131
|
+
{
|
|
132
|
+
providerId: directoryId
|
|
133
|
+
},
|
|
134
|
+
options?.projectId
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
async listUsers(options) {
|
|
139
|
+
return this.http.post(
|
|
140
|
+
"/api/auth/banata/scim/list-users",
|
|
141
|
+
this.http.withProjectScope(
|
|
142
|
+
{
|
|
143
|
+
providerId: options.directoryId,
|
|
144
|
+
state: options.state,
|
|
145
|
+
limit: options.limit,
|
|
146
|
+
before: options.before,
|
|
147
|
+
after: options.after
|
|
148
|
+
},
|
|
149
|
+
options.projectId
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
async getUser(directoryUserId, options) {
|
|
154
|
+
return this.http.post(
|
|
155
|
+
"/api/auth/banata/scim/get-user",
|
|
156
|
+
this.http.withProjectScope(
|
|
157
|
+
{
|
|
158
|
+
userId: directoryUserId
|
|
159
|
+
},
|
|
160
|
+
options?.projectId
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
async listGroups(options) {
|
|
165
|
+
return this.http.post(
|
|
166
|
+
"/api/auth/banata/scim/list-groups",
|
|
167
|
+
this.http.withProjectScope(
|
|
168
|
+
{
|
|
169
|
+
providerId: options.directoryId,
|
|
170
|
+
limit: options.limit,
|
|
171
|
+
before: options.before,
|
|
172
|
+
after: options.after
|
|
173
|
+
},
|
|
174
|
+
options.projectId
|
|
175
|
+
)
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
async getGroup(directoryGroupId, options) {
|
|
179
|
+
return this.http.post(
|
|
180
|
+
"/api/auth/banata/scim/get-group",
|
|
181
|
+
this.http.withProjectScope(
|
|
182
|
+
{
|
|
183
|
+
groupId: directoryGroupId
|
|
184
|
+
},
|
|
185
|
+
options?.projectId
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// src/resources/domains.ts
|
|
192
|
+
var Domains = class {
|
|
193
|
+
constructor(http) {
|
|
194
|
+
this.http = http;
|
|
195
|
+
}
|
|
196
|
+
async createVerification(options) {
|
|
197
|
+
return this.http.post(
|
|
198
|
+
"/api/auth/banata/domains/create",
|
|
199
|
+
this.http.withProjectScope(
|
|
200
|
+
{
|
|
201
|
+
organizationId: options.organizationId,
|
|
202
|
+
domain: options.domain
|
|
203
|
+
},
|
|
204
|
+
options.projectId
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
async getVerification(verificationId, options) {
|
|
209
|
+
return this.http.post(
|
|
210
|
+
"/api/auth/banata/domains/get",
|
|
211
|
+
this.http.withProjectScope(
|
|
212
|
+
{
|
|
213
|
+
id: verificationId
|
|
214
|
+
},
|
|
215
|
+
options?.projectId
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
async verify(verificationId, options) {
|
|
220
|
+
return this.http.post(
|
|
221
|
+
"/api/auth/banata/domains/verify",
|
|
222
|
+
this.http.withProjectScope(
|
|
223
|
+
{
|
|
224
|
+
id: verificationId
|
|
225
|
+
},
|
|
226
|
+
options?.projectId
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
async list(options) {
|
|
231
|
+
return this.http.post(
|
|
232
|
+
"/api/auth/banata/domains/list",
|
|
233
|
+
this.http.withProjectScope(
|
|
234
|
+
{
|
|
235
|
+
organizationId: options.organizationId,
|
|
236
|
+
limit: options.limit,
|
|
237
|
+
before: options.before,
|
|
238
|
+
after: options.after
|
|
239
|
+
},
|
|
240
|
+
options.projectId
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
async delete(verificationId, options) {
|
|
245
|
+
return this.http.post(
|
|
246
|
+
"/api/auth/banata/domains/delete",
|
|
247
|
+
this.http.withProjectScope(
|
|
248
|
+
{
|
|
249
|
+
id: verificationId
|
|
250
|
+
},
|
|
251
|
+
options?.projectId
|
|
252
|
+
)
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
// src/resources/emails.ts
|
|
258
|
+
var Emails = class {
|
|
259
|
+
constructor(http) {
|
|
260
|
+
this.http = http;
|
|
261
|
+
this.templates = new EmailTemplates(http);
|
|
262
|
+
}
|
|
263
|
+
templates;
|
|
264
|
+
/**
|
|
265
|
+
* Send a transactional email using a built-in or custom template.
|
|
266
|
+
*
|
|
267
|
+
* The email is rendered using the configured branding (colors, logo,
|
|
268
|
+
* app name) and sent via the active email provider configured in the
|
|
269
|
+
* dashboard.
|
|
270
|
+
*/
|
|
271
|
+
async send(options) {
|
|
272
|
+
return this.http.post("/api/auth/banata/emails/send", {
|
|
273
|
+
to: options.to,
|
|
274
|
+
template: options.template,
|
|
275
|
+
data: options.data
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Preview a rendered email template.
|
|
280
|
+
*
|
|
281
|
+
* Returns the subject, HTML, and plain text of the template with
|
|
282
|
+
* the current branding applied. Uses sample data if none provided.
|
|
283
|
+
*/
|
|
284
|
+
async preview(template, data) {
|
|
285
|
+
return this.http.post("/api/auth/banata/emails/preview", {
|
|
286
|
+
template,
|
|
287
|
+
data
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Send a test email to verify the email provider configuration.
|
|
292
|
+
*
|
|
293
|
+
* Uses the "welcome" template by default with sample data.
|
|
294
|
+
*/
|
|
295
|
+
async sendTest(to, template) {
|
|
296
|
+
return this.http.post("/api/auth/banata/test-email", {
|
|
297
|
+
to,
|
|
298
|
+
template
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
var EmailTemplates = class {
|
|
303
|
+
constructor(http) {
|
|
304
|
+
this.http = http;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* List all email templates, optionally filtered by category.
|
|
308
|
+
*/
|
|
309
|
+
async list(category) {
|
|
310
|
+
const payload = await this.http.post(
|
|
311
|
+
"/api/auth/banata/emails/templates/list",
|
|
312
|
+
{ category }
|
|
313
|
+
);
|
|
314
|
+
return payload.templates ?? [];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get a single email template by ID or slug.
|
|
318
|
+
*/
|
|
319
|
+
async get(idOrSlug) {
|
|
320
|
+
const payload = await this.http.post(
|
|
321
|
+
"/api/auth/banata/emails/templates/get",
|
|
322
|
+
{ idOrSlug }
|
|
323
|
+
);
|
|
324
|
+
return payload.template ?? null;
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Create a new custom email template.
|
|
328
|
+
*/
|
|
329
|
+
async create(options) {
|
|
330
|
+
const payload = await this.http.post("/api/auth/banata/emails/templates/create", options);
|
|
331
|
+
if (!payload.success) {
|
|
332
|
+
throw new Error(payload.error ?? "Failed to create template");
|
|
333
|
+
}
|
|
334
|
+
return payload.template;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Update an existing email template.
|
|
338
|
+
*/
|
|
339
|
+
async update(id, options) {
|
|
340
|
+
const payload = await this.http.post("/api/auth/banata/emails/templates/update", { id, ...options });
|
|
341
|
+
if (!payload.success) {
|
|
342
|
+
throw new Error(payload.error ?? "Failed to update template");
|
|
343
|
+
}
|
|
344
|
+
return payload.template;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Delete a custom email template.
|
|
348
|
+
* Built-in templates cannot be deleted.
|
|
349
|
+
*/
|
|
350
|
+
async delete(id) {
|
|
351
|
+
const payload = await this.http.post(
|
|
352
|
+
"/api/auth/banata/emails/templates/delete",
|
|
353
|
+
{ id }
|
|
354
|
+
);
|
|
355
|
+
if (!payload.success) {
|
|
356
|
+
throw new Error(payload.error ?? "Failed to delete template");
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/resources/events.ts
|
|
362
|
+
var Events = class {
|
|
363
|
+
constructor(http) {
|
|
364
|
+
this.http = http;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* List events with optional filters and cursor-based pagination.
|
|
368
|
+
*/
|
|
369
|
+
async listEvents(options) {
|
|
370
|
+
return this.http.post("/api/auth/banata/events/list", {
|
|
371
|
+
eventTypes: options?.eventTypes,
|
|
372
|
+
after: options?.after,
|
|
373
|
+
limit: options?.limit,
|
|
374
|
+
organizationId: options?.organizationId,
|
|
375
|
+
rangeStart: options?.rangeStart?.getTime(),
|
|
376
|
+
rangeEnd: options?.rangeEnd?.getTime()
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
// src/resources/organizations.ts
|
|
382
|
+
var Organizations = class {
|
|
383
|
+
constructor(http) {
|
|
384
|
+
this.http = http;
|
|
385
|
+
}
|
|
386
|
+
async listOrganizations(options) {
|
|
387
|
+
return this.http.post("/api/auth/organization/list", {
|
|
388
|
+
limit: options?.limit,
|
|
389
|
+
before: options?.before,
|
|
390
|
+
after: options?.after,
|
|
391
|
+
order: options?.order
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
async getOrganization(organizationId) {
|
|
395
|
+
return this.http.post("/api/auth/organization/get-full-organization", {
|
|
396
|
+
organizationId
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
async createOrganization(options) {
|
|
400
|
+
return this.http.post("/api/auth/organization/create", options);
|
|
401
|
+
}
|
|
402
|
+
async updateOrganization(options) {
|
|
403
|
+
const { organizationId, ...data } = options;
|
|
404
|
+
return this.http.post("/api/auth/organization/update", {
|
|
405
|
+
organizationId,
|
|
406
|
+
data
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
async deleteOrganization(organizationId) {
|
|
410
|
+
return this.http.post("/api/auth/organization/delete", {
|
|
411
|
+
organizationId
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
// ─── Members ───────────────────────────────────────────────────────────
|
|
415
|
+
async listMembers(options) {
|
|
416
|
+
return this.http.post(
|
|
417
|
+
"/api/auth/organization/list-members",
|
|
418
|
+
{
|
|
419
|
+
organizationId: options.organizationId,
|
|
420
|
+
role: options.role,
|
|
421
|
+
limit: options.limit,
|
|
422
|
+
before: options.before,
|
|
423
|
+
after: options.after
|
|
424
|
+
}
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
async removeMember(options) {
|
|
428
|
+
return this.http.post("/api/auth/organization/remove-member", options);
|
|
429
|
+
}
|
|
430
|
+
async updateMemberRole(options) {
|
|
431
|
+
return this.http.post("/api/auth/organization/update-member-role", options);
|
|
432
|
+
}
|
|
433
|
+
// ─── Invitations ───────────────────────────────────────────────────────
|
|
434
|
+
async sendInvitation(options) {
|
|
435
|
+
return this.http.post("/api/auth/organization/invite-member", options);
|
|
436
|
+
}
|
|
437
|
+
async revokeInvitation(invitationId) {
|
|
438
|
+
return this.http.post("/api/auth/organization/cancel-invitation", {
|
|
439
|
+
invitationId
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
async listInvitations(options) {
|
|
443
|
+
return this.http.post(
|
|
444
|
+
"/api/auth/organization/list-invitations",
|
|
445
|
+
{
|
|
446
|
+
organizationId: options.organizationId,
|
|
447
|
+
status: options.status,
|
|
448
|
+
limit: options.limit,
|
|
449
|
+
before: options.before,
|
|
450
|
+
after: options.after
|
|
451
|
+
}
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
// src/resources/portal.ts
|
|
457
|
+
var Portal = class {
|
|
458
|
+
constructor(http) {
|
|
459
|
+
this.http = http;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Generate a short-lived admin portal link for an organization's IT admin.
|
|
463
|
+
*
|
|
464
|
+
* @param options - Portal link generation options
|
|
465
|
+
* @returns The generated portal link and session metadata
|
|
466
|
+
*/
|
|
467
|
+
async generateLink(options) {
|
|
468
|
+
return this.http.post("/banata/portal/generate-link", options);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// src/resources/projects.ts
|
|
473
|
+
var Projects = class {
|
|
474
|
+
constructor(http) {
|
|
475
|
+
this.http = http;
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* List all projects.
|
|
479
|
+
*/
|
|
480
|
+
async listProjects() {
|
|
481
|
+
const res = await this.http.post("/api/auth/banata/projects/list");
|
|
482
|
+
return res.projects;
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Get a single project by ID.
|
|
486
|
+
*/
|
|
487
|
+
async getProject(id) {
|
|
488
|
+
const res = await this.http.post(
|
|
489
|
+
"/api/auth/banata/projects/get",
|
|
490
|
+
{ id }
|
|
491
|
+
);
|
|
492
|
+
return res.project;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Create a new project.
|
|
496
|
+
* Returns the project and seeds RBAC permissions + super_admin role.
|
|
497
|
+
*/
|
|
498
|
+
async createProject(data) {
|
|
499
|
+
return this.http.post("/api/auth/banata/projects/create", data);
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Update a project's metadata.
|
|
503
|
+
*/
|
|
504
|
+
async updateProject(id, update) {
|
|
505
|
+
return this.http.post("/api/auth/banata/projects/update", { id, ...update });
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Delete a project.
|
|
509
|
+
*
|
|
510
|
+
* Note: This deletes the project record only. Project-scoped data (users,
|
|
511
|
+
* organizations, etc.) is not automatically cascade-deleted. Use the
|
|
512
|
+
* `clearAllData` migration for a full reset.
|
|
513
|
+
*/
|
|
514
|
+
async deleteProject(id) {
|
|
515
|
+
return this.http.post("/api/auth/banata/projects/delete", { id });
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Ensure at least one project exists. If none exist, creates a
|
|
519
|
+
* "Default Project" with seeded RBAC permissions.
|
|
520
|
+
*/
|
|
521
|
+
async ensureDefaultProject() {
|
|
522
|
+
return this.http.post("/api/auth/banata/projects/ensure-default");
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// src/resources/rbac.ts
|
|
527
|
+
function normalizePermission(permission) {
|
|
528
|
+
if (typeof permission === "string") return permission;
|
|
529
|
+
return `${permission.resource}.${permission.action}`;
|
|
530
|
+
}
|
|
531
|
+
var Rbac = class {
|
|
532
|
+
constructor(http) {
|
|
533
|
+
this.http = http;
|
|
534
|
+
}
|
|
535
|
+
async listRoles() {
|
|
536
|
+
const result = await this.http.post(
|
|
537
|
+
"/api/auth/banata/config/roles/list",
|
|
538
|
+
{}
|
|
539
|
+
);
|
|
540
|
+
return result.roles ?? [];
|
|
541
|
+
}
|
|
542
|
+
async createRole(options) {
|
|
543
|
+
const result = await this.http.post(
|
|
544
|
+
"/api/auth/banata/config/roles/create",
|
|
545
|
+
options
|
|
546
|
+
);
|
|
547
|
+
return result.role;
|
|
548
|
+
}
|
|
549
|
+
async deleteRole(id) {
|
|
550
|
+
await this.http.post("/api/auth/banata/config/roles/delete", { id });
|
|
551
|
+
}
|
|
552
|
+
async listPermissions() {
|
|
553
|
+
const result = await this.http.post(
|
|
554
|
+
"/api/auth/banata/config/permissions/list",
|
|
555
|
+
{}
|
|
556
|
+
);
|
|
557
|
+
return result.permissions ?? [];
|
|
558
|
+
}
|
|
559
|
+
async createPermission(options) {
|
|
560
|
+
const result = await this.http.post(
|
|
561
|
+
"/api/auth/banata/config/permissions/create",
|
|
562
|
+
options
|
|
563
|
+
);
|
|
564
|
+
return result.permission;
|
|
565
|
+
}
|
|
566
|
+
async deletePermission(id) {
|
|
567
|
+
await this.http.post("/api/auth/banata/config/permissions/delete", { id });
|
|
568
|
+
}
|
|
569
|
+
async assignRole(options) {
|
|
570
|
+
await this.http.post("/api/auth/organization/update-member-role", {
|
|
571
|
+
memberIdOrUserId: options.userId,
|
|
572
|
+
role: options.role,
|
|
573
|
+
organizationId: options.organizationId
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
async checkPermission(options) {
|
|
577
|
+
return this.http.post("/api/auth/banata/rbac/check-permission", {
|
|
578
|
+
projectId: options.projectId,
|
|
579
|
+
permission: normalizePermission(options.permission)
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
async checkPermissions(options) {
|
|
583
|
+
return this.http.post("/api/auth/banata/rbac/check-permissions", {
|
|
584
|
+
projectId: options.projectId,
|
|
585
|
+
permissions: options.permissions.map(normalizePermission),
|
|
586
|
+
operator: options.operator ?? "all"
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
async getMyPermissions(projectId) {
|
|
590
|
+
const result = await this.http.post(
|
|
591
|
+
"/api/auth/banata/rbac/my-permissions",
|
|
592
|
+
{
|
|
593
|
+
projectId
|
|
594
|
+
}
|
|
595
|
+
);
|
|
596
|
+
return result.permissions ?? [];
|
|
597
|
+
}
|
|
598
|
+
async hasPermission(projectId, permission) {
|
|
599
|
+
const result = await this.checkPermission({
|
|
600
|
+
projectId,
|
|
601
|
+
permission
|
|
602
|
+
});
|
|
603
|
+
return result.allowed;
|
|
604
|
+
}
|
|
605
|
+
async requirePermission(projectId, permission) {
|
|
606
|
+
const allowed = await this.hasPermission(projectId, permission);
|
|
607
|
+
if (!allowed) {
|
|
608
|
+
throw new Error(`Missing permission: ${normalizePermission(permission)}`);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async revokeRole(options) {
|
|
612
|
+
await this.http.post("/api/auth/organization/update-member-role", {
|
|
613
|
+
memberIdOrUserId: options.userId,
|
|
614
|
+
role: options.fallbackRole ?? "super_admin",
|
|
615
|
+
organizationId: options.organizationId
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
// src/resources/sso.ts
|
|
621
|
+
var SSO = class {
|
|
622
|
+
constructor(http) {
|
|
623
|
+
this.http = http;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Get the authorization URL to redirect the user to for SSO.
|
|
627
|
+
* Supports routing by organization ID or callback URL.
|
|
628
|
+
*/
|
|
629
|
+
async getAuthorizationUrl(options) {
|
|
630
|
+
return this.http.post("/api/auth/sign-in/sso", {
|
|
631
|
+
providerId: options.connectionId,
|
|
632
|
+
organizationId: options.organizationId,
|
|
633
|
+
issuer: options.provider,
|
|
634
|
+
loginHint: options.loginHint,
|
|
635
|
+
domain: options.domainHint,
|
|
636
|
+
callbackURL: options.redirectUri
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* @deprecated Better Auth handles SSO token exchange internally via callback.
|
|
641
|
+
* This method is kept for backward compatibility but will throw.
|
|
642
|
+
*/
|
|
643
|
+
async getProfileAndToken(_options) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
"getProfileAndToken() is not supported with Better Auth. SSO token exchange is handled internally via the callback URL."
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
// ─── Connection Management ─────────────────────────────────────────────
|
|
649
|
+
async listConnections(options) {
|
|
650
|
+
return this.http.post(
|
|
651
|
+
"/api/auth/banata/sso/list-providers",
|
|
652
|
+
this.http.withProjectScope(
|
|
653
|
+
{
|
|
654
|
+
organizationId: options?.organizationId,
|
|
655
|
+
connectionType: options?.connectionType,
|
|
656
|
+
limit: options?.limit,
|
|
657
|
+
before: options?.before,
|
|
658
|
+
after: options?.after
|
|
659
|
+
},
|
|
660
|
+
options?.projectId
|
|
661
|
+
)
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
async getConnection(connectionId, options) {
|
|
665
|
+
return this.http.post(
|
|
666
|
+
"/api/auth/banata/sso/get-provider",
|
|
667
|
+
this.http.withProjectScope(
|
|
668
|
+
{
|
|
669
|
+
providerId: connectionId
|
|
670
|
+
},
|
|
671
|
+
options?.projectId
|
|
672
|
+
)
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
async createConnection(options) {
|
|
676
|
+
return this.http.post(
|
|
677
|
+
"/api/auth/banata/sso/register",
|
|
678
|
+
this.http.withProjectScope(
|
|
679
|
+
{
|
|
680
|
+
organizationId: options.organizationId,
|
|
681
|
+
type: options.type,
|
|
682
|
+
domain: options.domains?.[0],
|
|
683
|
+
name: options.name,
|
|
684
|
+
domains: options.domains,
|
|
685
|
+
samlConfig: options.samlConfig,
|
|
686
|
+
oidcConfig: options.oidcConfig
|
|
687
|
+
},
|
|
688
|
+
options.projectId
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
async updateConnection(options) {
|
|
693
|
+
const { connectionId, projectId, ...body } = options;
|
|
694
|
+
return this.http.post(
|
|
695
|
+
"/api/auth/banata/sso/update-provider",
|
|
696
|
+
this.http.withProjectScope(
|
|
697
|
+
{
|
|
698
|
+
providerId: connectionId,
|
|
699
|
+
...body
|
|
700
|
+
},
|
|
701
|
+
projectId
|
|
702
|
+
)
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
async deleteConnection(connectionId, options) {
|
|
706
|
+
return this.http.post(
|
|
707
|
+
"/api/auth/banata/sso/delete-provider",
|
|
708
|
+
this.http.withProjectScope(
|
|
709
|
+
{
|
|
710
|
+
providerId: connectionId
|
|
711
|
+
},
|
|
712
|
+
options?.projectId
|
|
713
|
+
)
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
async activateConnection(connectionId, options) {
|
|
717
|
+
return this.http.post(
|
|
718
|
+
"/api/auth/banata/sso/update-provider",
|
|
719
|
+
this.http.withProjectScope(
|
|
720
|
+
{
|
|
721
|
+
providerId: connectionId,
|
|
722
|
+
active: true
|
|
723
|
+
},
|
|
724
|
+
options?.projectId
|
|
725
|
+
)
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
async deactivateConnection(connectionId, options) {
|
|
729
|
+
return this.http.post(
|
|
730
|
+
"/api/auth/banata/sso/update-provider",
|
|
731
|
+
this.http.withProjectScope(
|
|
732
|
+
{
|
|
733
|
+
providerId: connectionId,
|
|
734
|
+
active: false
|
|
735
|
+
},
|
|
736
|
+
options?.projectId
|
|
737
|
+
)
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
// src/resources/user-management.ts
|
|
743
|
+
function emptyListMetadata() {
|
|
744
|
+
return {
|
|
745
|
+
before: null,
|
|
746
|
+
after: null
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
var UserManagement = class {
|
|
750
|
+
constructor(http) {
|
|
751
|
+
this.http = http;
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* List users with optional filtering and pagination.
|
|
755
|
+
*/
|
|
756
|
+
async listUsers(options) {
|
|
757
|
+
const payload = await this.http.post("/api/auth/admin/list-users", {
|
|
758
|
+
projectId: options?.projectId,
|
|
759
|
+
email: options?.email,
|
|
760
|
+
organizationId: options?.organizationId,
|
|
761
|
+
role: options?.role,
|
|
762
|
+
limit: options?.limit,
|
|
763
|
+
before: options?.before,
|
|
764
|
+
after: options?.after,
|
|
765
|
+
order: options?.order
|
|
766
|
+
});
|
|
767
|
+
return {
|
|
768
|
+
data: payload.data ?? payload.users ?? [],
|
|
769
|
+
listMetadata: emptyListMetadata()
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Get a user by ID.
|
|
774
|
+
*/
|
|
775
|
+
async getUser(userId, options) {
|
|
776
|
+
return this.http.post("/api/auth/admin/get-user", {
|
|
777
|
+
userId,
|
|
778
|
+
projectId: options?.projectId
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
/**
|
|
782
|
+
* Create a new user.
|
|
783
|
+
*/
|
|
784
|
+
async createUser(options) {
|
|
785
|
+
const payload = await this.http.post("/api/auth/admin/create-user", {
|
|
786
|
+
projectId: options.projectId,
|
|
787
|
+
email: options.email,
|
|
788
|
+
password: options.password,
|
|
789
|
+
name: options.name,
|
|
790
|
+
image: options.image,
|
|
791
|
+
username: options.username,
|
|
792
|
+
phoneNumber: options.phoneNumber,
|
|
793
|
+
emailVerified: options.emailVerified,
|
|
794
|
+
role: options.role ?? "user",
|
|
795
|
+
metadata: options.metadata,
|
|
796
|
+
data: options.data
|
|
797
|
+
});
|
|
798
|
+
return payload.user;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Update an existing user.
|
|
802
|
+
*/
|
|
803
|
+
async updateUser(options) {
|
|
804
|
+
const { userId, ...body } = options;
|
|
805
|
+
return this.http.post("/api/auth/admin/update-user", {
|
|
806
|
+
userId,
|
|
807
|
+
...body
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Delete a user.
|
|
812
|
+
*/
|
|
813
|
+
async deleteUser(userId, options) {
|
|
814
|
+
return this.http.post("/api/auth/admin/remove-user", {
|
|
815
|
+
userId,
|
|
816
|
+
projectId: options?.projectId
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Ban a user.
|
|
821
|
+
*/
|
|
822
|
+
async banUser(options) {
|
|
823
|
+
const payload = await this.http.post("/api/auth/admin/ban-user", {
|
|
824
|
+
userId: options.userId,
|
|
825
|
+
projectId: options.projectId,
|
|
826
|
+
banReason: options.reason,
|
|
827
|
+
banExpires: options.expiresAt?.getTime()
|
|
828
|
+
});
|
|
829
|
+
return payload.user;
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Unban a user.
|
|
833
|
+
*/
|
|
834
|
+
async unbanUser(userId, options) {
|
|
835
|
+
const payload = await this.http.post("/api/auth/admin/unban-user", {
|
|
836
|
+
userId,
|
|
837
|
+
projectId: options?.projectId
|
|
838
|
+
});
|
|
839
|
+
return payload.user;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* List active sessions for a user.
|
|
843
|
+
*/
|
|
844
|
+
async listUserSessions(userId, options) {
|
|
845
|
+
const payload = await this.http.post(
|
|
846
|
+
"/api/auth/admin/list-user-sessions",
|
|
847
|
+
{
|
|
848
|
+
userId,
|
|
849
|
+
projectId: options?.projectId
|
|
850
|
+
}
|
|
851
|
+
);
|
|
852
|
+
return {
|
|
853
|
+
data: payload.data ?? payload.sessions ?? [],
|
|
854
|
+
listMetadata: emptyListMetadata()
|
|
855
|
+
};
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Start impersonating a user.
|
|
859
|
+
*/
|
|
860
|
+
async impersonateUser(userId, options) {
|
|
861
|
+
return this.http.post("/api/auth/admin/impersonate-user", {
|
|
862
|
+
userId,
|
|
863
|
+
projectId: options?.projectId
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Stop impersonating the current user.
|
|
868
|
+
*/
|
|
869
|
+
async stopImpersonating() {
|
|
870
|
+
return this.http.post("/api/auth/admin/stop-impersonating");
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Revoke a specific session.
|
|
874
|
+
*/
|
|
875
|
+
async revokeSession(sessionId, options) {
|
|
876
|
+
return this.http.post("/api/auth/admin/revoke-user-session", {
|
|
877
|
+
sessionId,
|
|
878
|
+
projectId: options?.projectId
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Revoke all sessions for a user.
|
|
883
|
+
*/
|
|
884
|
+
async revokeAllSessions(userId, options) {
|
|
885
|
+
return this.http.post("/api/auth/admin/revoke-user-sessions", {
|
|
886
|
+
userId,
|
|
887
|
+
projectId: options?.projectId
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Set a user's global role.
|
|
892
|
+
*/
|
|
893
|
+
async setRole(userId, role, options) {
|
|
894
|
+
const payload = await this.http.post("/api/auth/admin/set-role", {
|
|
895
|
+
userId,
|
|
896
|
+
role,
|
|
897
|
+
projectId: options?.projectId
|
|
898
|
+
});
|
|
899
|
+
return payload.user;
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Force-set a user's password.
|
|
903
|
+
*/
|
|
904
|
+
async setUserPassword(userId, newPassword, options) {
|
|
905
|
+
const payload = await this.http.post("/api/auth/admin/set-user-password", {
|
|
906
|
+
userId,
|
|
907
|
+
newPassword,
|
|
908
|
+
projectId: options?.projectId
|
|
909
|
+
});
|
|
910
|
+
return payload.status === true;
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Check whether a user or role has the requested permissions.
|
|
914
|
+
*/
|
|
915
|
+
async hasPermission(options) {
|
|
916
|
+
const payload = await this.http.post("/api/auth/admin/has-permission", {
|
|
917
|
+
...options
|
|
918
|
+
});
|
|
919
|
+
return payload.success === true;
|
|
920
|
+
}
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
// src/resources/vault.ts
|
|
924
|
+
var Vault = class {
|
|
925
|
+
constructor(http) {
|
|
926
|
+
this.http = http;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* Encrypt and store a secret.
|
|
930
|
+
*
|
|
931
|
+
* @returns The ID of the stored secret.
|
|
932
|
+
*/
|
|
933
|
+
async encrypt(options) {
|
|
934
|
+
return this.http.post("/api/auth/banata/vault/encrypt", options);
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Decrypt and return a secret by ID.
|
|
938
|
+
*
|
|
939
|
+
* @param options.context - Must match the context used during encryption.
|
|
940
|
+
*/
|
|
941
|
+
async decrypt(options) {
|
|
942
|
+
return this.http.post("/api/auth/banata/vault/decrypt", options);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* List vault secrets (metadata only — decrypted values are never returned).
|
|
946
|
+
*/
|
|
947
|
+
async list(options) {
|
|
948
|
+
return this.http.post("/api/auth/banata/vault/list", {
|
|
949
|
+
organizationId: options?.organizationId,
|
|
950
|
+
limit: options?.limit,
|
|
951
|
+
before: options?.before,
|
|
952
|
+
after: options?.after
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Delete a vault secret by ID.
|
|
957
|
+
*/
|
|
958
|
+
async delete(options) {
|
|
959
|
+
return this.http.post("/api/auth/banata/vault/delete", {
|
|
960
|
+
id: options.secretId
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* Rotate the encryption key by re-encrypting all secrets
|
|
965
|
+
* with the next version's derived key.
|
|
966
|
+
*
|
|
967
|
+
* @returns Status and count of rotated/failed secrets.
|
|
968
|
+
*/
|
|
969
|
+
async rotateKey(options) {
|
|
970
|
+
return this.http.post("/api/auth/banata/vault/rotate-key", {
|
|
971
|
+
batchSize: options?.batchSize
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
// src/resources/webhooks.ts
|
|
977
|
+
var Webhooks = class {
|
|
978
|
+
constructor(http) {
|
|
979
|
+
this.http = http;
|
|
980
|
+
}
|
|
981
|
+
// ─── Endpoint Management ───────────────────────────────────────────────
|
|
982
|
+
async listEndpoints(options) {
|
|
983
|
+
return this.http.post("/api/auth/banata/webhooks/list", {
|
|
984
|
+
limit: options?.limit,
|
|
985
|
+
before: options?.before,
|
|
986
|
+
after: options?.after
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
async createEndpoint(options) {
|
|
990
|
+
return this.http.post("/api/auth/banata/webhooks/create", options);
|
|
991
|
+
}
|
|
992
|
+
async updateEndpoint(options) {
|
|
993
|
+
const { endpointId, ...body } = options;
|
|
994
|
+
return this.http.post("/api/auth/banata/webhooks/update", {
|
|
995
|
+
id: endpointId,
|
|
996
|
+
...body
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
async deleteEndpoint(endpointId) {
|
|
1000
|
+
return this.http.post("/api/auth/banata/webhooks/delete", {
|
|
1001
|
+
id: endpointId
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
// ─── Signature Verification ────────────────────────────────────────────
|
|
1005
|
+
/**
|
|
1006
|
+
* Verify a webhook signature and construct the event payload.
|
|
1007
|
+
* Uses the Web Crypto API for HMAC-SHA256 signature verification.
|
|
1008
|
+
*
|
|
1009
|
+
* @example
|
|
1010
|
+
* ```ts
|
|
1011
|
+
* const event = await banataAuth.webhooks.constructEvent({
|
|
1012
|
+
* payload: req.body,
|
|
1013
|
+
* sigHeader: req.headers["x-banataauth-signature"],
|
|
1014
|
+
* secret: webhookSecret,
|
|
1015
|
+
* });
|
|
1016
|
+
* ```
|
|
1017
|
+
*/
|
|
1018
|
+
async constructEvent(options) {
|
|
1019
|
+
const { payload, sigHeader, secret, tolerance = 300 } = options;
|
|
1020
|
+
if (!await this.verifySignature({ payload, sigHeader, secret, tolerance })) {
|
|
1021
|
+
throw new Error("Invalid webhook signature");
|
|
1022
|
+
}
|
|
1023
|
+
return JSON.parse(payload);
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Verify a webhook signature using the Web Crypto API (HMAC-SHA256).
|
|
1027
|
+
*/
|
|
1028
|
+
async verifySignature(options) {
|
|
1029
|
+
const { payload, sigHeader, secret, tolerance = 300 } = options;
|
|
1030
|
+
const parts = sigHeader.split(",");
|
|
1031
|
+
const timestampPart = parts.find((p) => p.startsWith("t="));
|
|
1032
|
+
const signaturePart = parts.find((p) => p.startsWith("v1="));
|
|
1033
|
+
if (!timestampPart || !signaturePart) {
|
|
1034
|
+
return false;
|
|
1035
|
+
}
|
|
1036
|
+
const timestamp = Number.parseInt(timestampPart.slice(2), 10);
|
|
1037
|
+
const signature = signaturePart.slice(3);
|
|
1038
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1039
|
+
if (Math.abs(now - timestamp) > tolerance) {
|
|
1040
|
+
return false;
|
|
1041
|
+
}
|
|
1042
|
+
const signedPayload = `${timestamp}.${payload}`;
|
|
1043
|
+
const expectedSignature = await this.computeHmacAsync(secret, signedPayload);
|
|
1044
|
+
return this.timingSafeEqual(signature, expectedSignature);
|
|
1045
|
+
}
|
|
1046
|
+
async computeHmacAsync(secret, payload) {
|
|
1047
|
+
const encoder = new TextEncoder();
|
|
1048
|
+
const key = await crypto.subtle.importKey(
|
|
1049
|
+
"raw",
|
|
1050
|
+
encoder.encode(secret),
|
|
1051
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1052
|
+
false,
|
|
1053
|
+
["sign"]
|
|
1054
|
+
);
|
|
1055
|
+
const signatureBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(payload));
|
|
1056
|
+
return Array.from(new Uint8Array(signatureBuffer)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1057
|
+
}
|
|
1058
|
+
timingSafeEqual(a, b) {
|
|
1059
|
+
if (a.length !== b.length) return false;
|
|
1060
|
+
let result = 0;
|
|
1061
|
+
for (let i = 0; i < a.length; i++) {
|
|
1062
|
+
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
1063
|
+
}
|
|
1064
|
+
return result === 0;
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
|
|
1068
|
+
// src/client.ts
|
|
1069
|
+
var HttpClient = class {
|
|
1070
|
+
apiKey;
|
|
1071
|
+
baseUrl;
|
|
1072
|
+
timeout;
|
|
1073
|
+
maxRetries;
|
|
1074
|
+
projectId;
|
|
1075
|
+
constructor(options) {
|
|
1076
|
+
this.apiKey = options.apiKey;
|
|
1077
|
+
this.baseUrl = (options.baseUrl ?? "").replace(/\/$/, "");
|
|
1078
|
+
this.timeout = options.timeout ?? 3e4;
|
|
1079
|
+
this.maxRetries = options.retries ?? 3;
|
|
1080
|
+
this.projectId = options.projectId;
|
|
1081
|
+
if (this.baseUrl && !this.baseUrl.startsWith("https://") && !this.baseUrl.startsWith("http://localhost") && !this.baseUrl.startsWith("http://127.0.0.1")) {
|
|
1082
|
+
console.warn(
|
|
1083
|
+
"[BanataAuth SDK] WARNING: baseUrl does not use HTTPS. API keys will be transmitted in plaintext. Use HTTPS in production to protect your credentials."
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
withProjectScope(body, projectId) {
|
|
1088
|
+
const resolvedProjectId = projectId ?? this.projectId;
|
|
1089
|
+
if (!resolvedProjectId) {
|
|
1090
|
+
return body;
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
...body,
|
|
1094
|
+
projectId: resolvedProjectId
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
async request(method, path, options) {
|
|
1098
|
+
const url = new URL(`${this.baseUrl}${path}`);
|
|
1099
|
+
if (options?.query) {
|
|
1100
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
1101
|
+
if (value !== void 0) {
|
|
1102
|
+
url.searchParams.set(key, String(value));
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
const headers = {
|
|
1107
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
1108
|
+
"Content-Type": "application/json",
|
|
1109
|
+
"User-Agent": "banata-auth-sdk/0.1.0",
|
|
1110
|
+
...options?.headers
|
|
1111
|
+
};
|
|
1112
|
+
let lastError = null;
|
|
1113
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
1114
|
+
try {
|
|
1115
|
+
const controller = new AbortController();
|
|
1116
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1117
|
+
const response = await fetch(url.toString(), {
|
|
1118
|
+
method,
|
|
1119
|
+
headers,
|
|
1120
|
+
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
1121
|
+
signal: controller.signal
|
|
1122
|
+
});
|
|
1123
|
+
clearTimeout(timeoutId);
|
|
1124
|
+
const requestId = response.headers.get("x-request-id") ?? "";
|
|
1125
|
+
if (response.ok) {
|
|
1126
|
+
if (response.status === 204) {
|
|
1127
|
+
return void 0;
|
|
1128
|
+
}
|
|
1129
|
+
return await response.json();
|
|
1130
|
+
}
|
|
1131
|
+
const errorBody = await response.json().catch(() => ({}));
|
|
1132
|
+
const error = createErrorFromStatus(
|
|
1133
|
+
response.status,
|
|
1134
|
+
errorBody,
|
|
1135
|
+
requestId
|
|
1136
|
+
);
|
|
1137
|
+
if ((response.status >= 500 || response.status === 429) && attempt < this.maxRetries) {
|
|
1138
|
+
lastError = error;
|
|
1139
|
+
let delayMs = 200 * 2 ** attempt;
|
|
1140
|
+
if (response.status === 429) {
|
|
1141
|
+
const retryAfter = response.headers.get("retry-after");
|
|
1142
|
+
if (retryAfter) {
|
|
1143
|
+
const parsed = Number(retryAfter);
|
|
1144
|
+
delayMs = Number.isNaN(parsed) ? Math.max(0, new Date(retryAfter).getTime() - Date.now()) : parsed * 1e3;
|
|
1145
|
+
delayMs = Math.min(delayMs, 6e4);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1149
|
+
continue;
|
|
1150
|
+
}
|
|
1151
|
+
throw error;
|
|
1152
|
+
} catch (error) {
|
|
1153
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1154
|
+
lastError = new Error(`Request timed out after ${this.timeout}ms`);
|
|
1155
|
+
if (attempt < this.maxRetries) {
|
|
1156
|
+
await new Promise((resolve) => setTimeout(resolve, 200 * 2 ** attempt));
|
|
1157
|
+
continue;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
throw error;
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
throw lastError ?? new Error("Request failed after all retries");
|
|
1164
|
+
}
|
|
1165
|
+
get(path, query) {
|
|
1166
|
+
return this.request("GET", path, { query });
|
|
1167
|
+
}
|
|
1168
|
+
post(path, body) {
|
|
1169
|
+
return this.request("POST", path, { body });
|
|
1170
|
+
}
|
|
1171
|
+
put(path, body) {
|
|
1172
|
+
return this.request("PUT", path, { body });
|
|
1173
|
+
}
|
|
1174
|
+
patch(path, body) {
|
|
1175
|
+
return this.request("PATCH", path, { body });
|
|
1176
|
+
}
|
|
1177
|
+
delete(path) {
|
|
1178
|
+
return this.request("DELETE", path);
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
var BanataAuth = class {
|
|
1182
|
+
httpClient;
|
|
1183
|
+
apiKeys;
|
|
1184
|
+
userManagement;
|
|
1185
|
+
organizations;
|
|
1186
|
+
sso;
|
|
1187
|
+
directorySync;
|
|
1188
|
+
auditLogs;
|
|
1189
|
+
emails;
|
|
1190
|
+
events;
|
|
1191
|
+
webhooks;
|
|
1192
|
+
portal;
|
|
1193
|
+
vault;
|
|
1194
|
+
domains;
|
|
1195
|
+
rbac;
|
|
1196
|
+
projects;
|
|
1197
|
+
constructor(options) {
|
|
1198
|
+
const opts = typeof options === "string" ? { apiKey: options } : options;
|
|
1199
|
+
if (!opts.apiKey) {
|
|
1200
|
+
throw new Error(
|
|
1201
|
+
"Banata Auth API key is required. Pass it as a string or as { apiKey: '...' }"
|
|
1202
|
+
);
|
|
1203
|
+
}
|
|
1204
|
+
this.httpClient = new HttpClient(opts);
|
|
1205
|
+
this.apiKeys = new ApiKeys(this.httpClient);
|
|
1206
|
+
this.userManagement = new UserManagement(this.httpClient);
|
|
1207
|
+
this.organizations = new Organizations(this.httpClient);
|
|
1208
|
+
this.sso = new SSO(this.httpClient);
|
|
1209
|
+
this.directorySync = new DirectorySync(this.httpClient);
|
|
1210
|
+
this.auditLogs = new AuditLogs(this.httpClient);
|
|
1211
|
+
this.emails = new Emails(this.httpClient);
|
|
1212
|
+
this.events = new Events(this.httpClient);
|
|
1213
|
+
this.webhooks = new Webhooks(this.httpClient);
|
|
1214
|
+
this.portal = new Portal(this.httpClient);
|
|
1215
|
+
this.vault = new Vault(this.httpClient);
|
|
1216
|
+
this.domains = new Domains(this.httpClient);
|
|
1217
|
+
this.rbac = new Rbac(this.httpClient);
|
|
1218
|
+
this.projects = new Projects(this.httpClient);
|
|
1219
|
+
}
|
|
1220
|
+
// Convenience aliases (WorkOS-compatible)
|
|
1221
|
+
get users() {
|
|
1222
|
+
return this.userManagement;
|
|
1223
|
+
}
|
|
1224
|
+
get directories() {
|
|
1225
|
+
return this.directorySync;
|
|
1226
|
+
}
|
|
1227
|
+
get orgs() {
|
|
1228
|
+
return this.organizations;
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
export { ApiKeys, AuditLogs, BanataAuth, DirectorySync, Domains, Emails, Events, Organizations, Portal, Projects, Rbac, SSO, UserManagement, Vault, Webhooks };
|
|
1233
|
+
//# sourceMappingURL=index.js.map
|
|
1234
|
+
//# sourceMappingURL=index.js.map
|