@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/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