@drmhse/sso-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,1386 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ AuthModule: () => AuthModule,
24
+ InvitationsModule: () => InvitationsModule,
25
+ OrganizationsModule: () => OrganizationsModule,
26
+ PlatformModule: () => PlatformModule,
27
+ ServicesModule: () => ServicesModule,
28
+ SsoApiError: () => SsoApiError,
29
+ SsoClient: () => SsoClient,
30
+ UserModule: () => UserModule
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/errors.ts
35
+ var SsoApiError = class _SsoApiError extends Error {
36
+ constructor(message, statusCode, errorCode, timestamp) {
37
+ super(message);
38
+ this.name = "SsoApiError";
39
+ this.statusCode = statusCode;
40
+ this.errorCode = errorCode;
41
+ this.timestamp = timestamp;
42
+ if (Error.captureStackTrace) {
43
+ Error.captureStackTrace(this, _SsoApiError);
44
+ }
45
+ }
46
+ /**
47
+ * Check if the error is a specific error code.
48
+ */
49
+ is(errorCode) {
50
+ return this.errorCode === errorCode;
51
+ }
52
+ /**
53
+ * Check if the error is an authentication error.
54
+ */
55
+ isAuthError() {
56
+ return this.statusCode === 401 || this.errorCode === "UNAUTHORIZED" || this.errorCode === "TOKEN_EXPIRED";
57
+ }
58
+ /**
59
+ * Check if the error is a permission error.
60
+ */
61
+ isForbidden() {
62
+ return this.statusCode === 403 || this.errorCode === "FORBIDDEN";
63
+ }
64
+ /**
65
+ * Check if the error is a not found error.
66
+ */
67
+ isNotFound() {
68
+ return this.statusCode === 404 || this.errorCode === "NOT_FOUND";
69
+ }
70
+ };
71
+
72
+ // src/http.ts
73
+ var HttpClient = class {
74
+ constructor(baseURL) {
75
+ this.defaults = {
76
+ baseURL,
77
+ headers: {
78
+ common: {
79
+ "Content-Type": "application/json"
80
+ }
81
+ },
82
+ timeout: 3e4
83
+ };
84
+ }
85
+ /**
86
+ * Build query string from params object
87
+ */
88
+ buildQueryString(params) {
89
+ if (!params) return "";
90
+ const searchParams = new URLSearchParams();
91
+ Object.entries(params).forEach(([key, value]) => {
92
+ if (value !== void 0 && value !== null) {
93
+ searchParams.append(key, String(value));
94
+ }
95
+ });
96
+ const queryString = searchParams.toString();
97
+ return queryString ? `?${queryString}` : "";
98
+ }
99
+ /**
100
+ * Build full URL from path and params
101
+ */
102
+ buildUrl(path, params) {
103
+ const baseUrl = this.defaults.baseURL.replace(/\/$/, "");
104
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
105
+ const queryString = this.buildQueryString(params);
106
+ return `${baseUrl}${cleanPath}${queryString}`;
107
+ }
108
+ /**
109
+ * Make HTTP request with timeout support
110
+ */
111
+ async request(path, options) {
112
+ const url = this.buildUrl(path, options.params);
113
+ const timeout = options.timeout ?? this.defaults.timeout;
114
+ const headers = {
115
+ ...this.defaults.headers.common,
116
+ ...options.headers
117
+ };
118
+ const controller = new AbortController();
119
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
120
+ try {
121
+ const response = await fetch(url, {
122
+ method: options.method,
123
+ headers,
124
+ body: options.body ? JSON.stringify(options.body) : void 0,
125
+ signal: controller.signal
126
+ });
127
+ clearTimeout(timeoutId);
128
+ let data;
129
+ const contentType = response.headers.get("content-type");
130
+ if (contentType?.includes("application/json")) {
131
+ data = await response.json();
132
+ } else {
133
+ const text = await response.text();
134
+ data = text || void 0;
135
+ }
136
+ if (!response.ok) {
137
+ if (data && data.error_code && data.error && data.timestamp) {
138
+ throw new SsoApiError(data.error, response.status, data.error_code, data.timestamp);
139
+ }
140
+ throw new SsoApiError(
141
+ data?.message || `HTTP ${response.status}: ${response.statusText}`,
142
+ response.status,
143
+ "UNKNOWN_ERROR",
144
+ (/* @__PURE__ */ new Date()).toISOString()
145
+ );
146
+ }
147
+ return {
148
+ data,
149
+ status: response.status,
150
+ headers: response.headers
151
+ };
152
+ } catch (error) {
153
+ clearTimeout(timeoutId);
154
+ if (error.name === "AbortError") {
155
+ throw new SsoApiError("Request timeout", 408, "TIMEOUT", (/* @__PURE__ */ new Date()).toISOString());
156
+ }
157
+ if (error instanceof TypeError && error.message.includes("fetch")) {
158
+ throw new SsoApiError(
159
+ "Network error - unable to reach the server",
160
+ 0,
161
+ "NETWORK_ERROR",
162
+ (/* @__PURE__ */ new Date()).toISOString()
163
+ );
164
+ }
165
+ if (error instanceof SsoApiError) {
166
+ throw error;
167
+ }
168
+ throw new SsoApiError(
169
+ error.message || "An unexpected error occurred",
170
+ 500,
171
+ "UNKNOWN_ERROR",
172
+ (/* @__PURE__ */ new Date()).toISOString()
173
+ );
174
+ }
175
+ }
176
+ /**
177
+ * GET request
178
+ */
179
+ async get(path, config) {
180
+ return this.request(path, {
181
+ method: "GET",
182
+ params: config?.params,
183
+ headers: config?.headers
184
+ });
185
+ }
186
+ /**
187
+ * POST request
188
+ */
189
+ async post(path, data, config) {
190
+ return this.request(path, {
191
+ method: "POST",
192
+ body: data,
193
+ headers: config?.headers
194
+ });
195
+ }
196
+ /**
197
+ * PATCH request
198
+ */
199
+ async patch(path, data, config) {
200
+ return this.request(path, {
201
+ method: "PATCH",
202
+ body: data,
203
+ headers: config?.headers
204
+ });
205
+ }
206
+ /**
207
+ * DELETE request
208
+ */
209
+ async delete(path, config) {
210
+ return this.request(path, {
211
+ method: "DELETE",
212
+ headers: config?.headers
213
+ });
214
+ }
215
+ };
216
+ function createHttpAgent(baseURL) {
217
+ return new HttpClient(baseURL);
218
+ }
219
+
220
+ // src/modules/analytics.ts
221
+ var AnalyticsModule = class {
222
+ constructor(http) {
223
+ this.http = http;
224
+ }
225
+ /**
226
+ * Get login trends over time.
227
+ * Returns daily login counts grouped by date.
228
+ *
229
+ * @param orgSlug Organization slug
230
+ * @param params Optional query parameters (date range)
231
+ * @returns Array of login trend data points
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * const trends = await sso.analytics.getLoginTrends('acme-corp', {
236
+ * start_date: '2025-01-01',
237
+ * end_date: '2025-01-31'
238
+ * });
239
+ * trends.forEach(point => console.log(point.date, point.count));
240
+ * ```
241
+ */
242
+ async getLoginTrends(orgSlug, params) {
243
+ const response = await this.http.get(
244
+ `/api/organizations/${orgSlug}/analytics/login-trends`,
245
+ { params }
246
+ );
247
+ return response.data;
248
+ }
249
+ /**
250
+ * Get login counts grouped by service.
251
+ * Shows which services have the most authentication activity.
252
+ *
253
+ * @param orgSlug Organization slug
254
+ * @param params Optional query parameters (date range)
255
+ * @returns Array of login counts per service
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const byService = await sso.analytics.getLoginsByService('acme-corp', {
260
+ * start_date: '2025-01-01',
261
+ * end_date: '2025-01-31'
262
+ * });
263
+ * byService.forEach(s => console.log(s.service_name, s.count));
264
+ * ```
265
+ */
266
+ async getLoginsByService(orgSlug, params) {
267
+ const response = await this.http.get(
268
+ `/api/organizations/${orgSlug}/analytics/logins-by-service`,
269
+ { params }
270
+ );
271
+ return response.data;
272
+ }
273
+ /**
274
+ * Get login counts grouped by OAuth provider.
275
+ * Shows which authentication providers are being used (GitHub, Google, Microsoft).
276
+ *
277
+ * @param orgSlug Organization slug
278
+ * @param params Optional query parameters (date range)
279
+ * @returns Array of login counts per provider
280
+ *
281
+ * @example
282
+ * ```typescript
283
+ * const byProvider = await sso.analytics.getLoginsByProvider('acme-corp', {
284
+ * start_date: '2025-01-01',
285
+ * end_date: '2025-01-31'
286
+ * });
287
+ * byProvider.forEach(p => console.log(p.provider, p.count));
288
+ * ```
289
+ */
290
+ async getLoginsByProvider(orgSlug, params) {
291
+ const response = await this.http.get(
292
+ `/api/organizations/${orgSlug}/analytics/logins-by-provider`,
293
+ { params }
294
+ );
295
+ return response.data;
296
+ }
297
+ /**
298
+ * Get the most recent login events.
299
+ *
300
+ * @param orgSlug Organization slug
301
+ * @param params Optional query parameters (limit)
302
+ * @returns Array of recent login events
303
+ *
304
+ * @example
305
+ * ```typescript
306
+ * const recentLogins = await sso.analytics.getRecentLogins('acme-corp', {
307
+ * limit: 10
308
+ * });
309
+ * recentLogins.forEach(login => {
310
+ * console.log(login.user_id, login.provider, login.created_at);
311
+ * });
312
+ * ```
313
+ */
314
+ async getRecentLogins(orgSlug, params) {
315
+ const response = await this.http.get(
316
+ `/api/organizations/${orgSlug}/analytics/recent-logins`,
317
+ { params }
318
+ );
319
+ return response.data;
320
+ }
321
+ };
322
+
323
+ // src/modules/auth.ts
324
+ var AuthModule = class {
325
+ constructor(http) {
326
+ this.http = http;
327
+ /**
328
+ * Device Flow: Request a device code for CLI/device authentication.
329
+ *
330
+ * @param payload Device code request payload
331
+ * @returns Device code response with user code and verification URI
332
+ *
333
+ * @example
334
+ * ```typescript
335
+ * const response = await sso.auth.deviceCode.request({
336
+ * client_id: 'service-client-id',
337
+ * org: 'acme-corp',
338
+ * service: 'acme-cli'
339
+ * });
340
+ * console.log(`Visit ${response.verification_uri} and enter code: ${response.user_code}`);
341
+ * ```
342
+ */
343
+ this.deviceCode = {
344
+ /**
345
+ * Request a device code
346
+ */
347
+ request: async (payload) => {
348
+ const response = await this.http.post("/auth/device/code", payload);
349
+ return response.data;
350
+ },
351
+ /**
352
+ * Exchange a device code for a JWT token.
353
+ * This should be polled by the device/CLI after displaying the user code.
354
+ *
355
+ * @param payload Token request payload
356
+ * @returns Token response with JWT
357
+ *
358
+ * @example
359
+ * ```typescript
360
+ * // Poll every 5 seconds
361
+ * const interval = setInterval(async () => {
362
+ * try {
363
+ * const token = await sso.auth.deviceCode.exchangeToken({
364
+ * grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
365
+ * device_code: deviceCode,
366
+ * client_id: 'service-client-id'
367
+ * });
368
+ * clearInterval(interval);
369
+ * sso.setAuthToken(token.access_token);
370
+ * } catch (error) {
371
+ * if (error.errorCode !== 'authorization_pending') {
372
+ * clearInterval(interval);
373
+ * throw error;
374
+ * }
375
+ * }
376
+ * }, 5000);
377
+ * ```
378
+ */
379
+ exchangeToken: async (payload) => {
380
+ const response = await this.http.post("/auth/token", payload);
381
+ return response.data;
382
+ }
383
+ };
384
+ }
385
+ /**
386
+ * Constructs the OAuth login URL for end-users.
387
+ * This does not perform the redirect; the consuming application
388
+ * should redirect the user's browser to this URL.
389
+ *
390
+ * @param provider The OAuth provider to use
391
+ * @param params Login parameters (org, service, redirect_uri)
392
+ * @returns The full URL to redirect the user to
393
+ *
394
+ * @example
395
+ * ```typescript
396
+ * const url = sso.auth.getLoginUrl('github', {
397
+ * org: 'acme-corp',
398
+ * service: 'main-app',
399
+ * redirect_uri: 'https://app.acme.com/callback'
400
+ * });
401
+ * window.location.href = url;
402
+ * ```
403
+ */
404
+ getLoginUrl(provider, params) {
405
+ const baseURL = this.http.defaults.baseURL || "";
406
+ const searchParams = new URLSearchParams({
407
+ org: params.org,
408
+ service: params.service
409
+ });
410
+ if (params.redirect_uri) {
411
+ searchParams.append("redirect_uri", params.redirect_uri);
412
+ }
413
+ return `${baseURL}/auth/${provider}?${searchParams.toString()}`;
414
+ }
415
+ /**
416
+ * Constructs the OAuth login URL for platform/organization admins.
417
+ * This uses the platform's dedicated OAuth credentials.
418
+ *
419
+ * @param provider The OAuth provider to use
420
+ * @param params Optional admin login parameters
421
+ * @returns The full URL to redirect the admin to
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * const url = sso.auth.getAdminLoginUrl('github', {
426
+ * org_slug: 'acme-corp'
427
+ * });
428
+ * window.location.href = url;
429
+ * ```
430
+ */
431
+ getAdminLoginUrl(provider, params) {
432
+ const baseURL = this.http.defaults.baseURL || "";
433
+ const searchParams = new URLSearchParams();
434
+ if (params?.org_slug) {
435
+ searchParams.append("org_slug", params.org_slug);
436
+ }
437
+ const queryString = searchParams.toString();
438
+ return `${baseURL}/auth/admin/${provider}${queryString ? `?${queryString}` : ""}`;
439
+ }
440
+ /**
441
+ * Logout the current user by revoking their JWT.
442
+ * After calling this, you should clear the token from storage
443
+ * and call `sso.setAuthToken(null)`.
444
+ *
445
+ * @example
446
+ * ```typescript
447
+ * await sso.auth.logout();
448
+ * sso.setAuthToken(null);
449
+ * localStorage.removeItem('jwt');
450
+ * ```
451
+ */
452
+ async logout() {
453
+ await this.http.post("/api/auth/logout");
454
+ }
455
+ /**
456
+ * Get a fresh provider access token for the authenticated user.
457
+ * This will automatically refresh the token if it's expired.
458
+ *
459
+ * @param provider The OAuth provider
460
+ * @returns Fresh provider token
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * const token = await sso.auth.getProviderToken('github');
465
+ * // Use token.access_token to make GitHub API calls
466
+ * ```
467
+ */
468
+ async getProviderToken(provider) {
469
+ const response = await this.http.get(`/api/provider-token/${provider}`);
470
+ return response.data;
471
+ }
472
+ };
473
+
474
+ // src/modules/user.ts
475
+ var IdentitiesModule = class {
476
+ constructor(http) {
477
+ this.http = http;
478
+ }
479
+ /**
480
+ * List all social accounts linked to the authenticated user.
481
+ *
482
+ * @returns Array of linked identities
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * const identities = await sso.user.identities.list();
487
+ * console.log(identities); // [{ provider: 'github' }, { provider: 'google' }]
488
+ * ```
489
+ */
490
+ async list() {
491
+ const response = await this.http.get("/api/user/identities");
492
+ return response.data;
493
+ }
494
+ /**
495
+ * Start linking a new social account to the authenticated user.
496
+ * Returns an authorization URL that the user should be redirected to.
497
+ *
498
+ * @param provider The OAuth provider to link (e.g., 'github', 'google', 'microsoft')
499
+ * @returns Object containing the authorization URL
500
+ *
501
+ * @example
502
+ * ```typescript
503
+ * const { authorization_url } = await sso.user.identities.startLink('github');
504
+ * window.location.href = authorization_url; // Redirect user to complete OAuth
505
+ * ```
506
+ */
507
+ async startLink(provider) {
508
+ const response = await this.http.post(`/api/user/identities/${provider}/link`, {});
509
+ return response.data;
510
+ }
511
+ /**
512
+ * Unlink a social account from the authenticated user.
513
+ * Note: Cannot unlink the last remaining identity to prevent account lockout.
514
+ *
515
+ * @param provider The OAuth provider to unlink (e.g., 'github', 'google', 'microsoft')
516
+ *
517
+ * @example
518
+ * ```typescript
519
+ * await sso.user.identities.unlink('google');
520
+ * ```
521
+ */
522
+ async unlink(provider) {
523
+ await this.http.delete(`/api/user/identities/${provider}`);
524
+ }
525
+ };
526
+ var UserModule = class {
527
+ constructor(http) {
528
+ this.http = http;
529
+ this.identities = new IdentitiesModule(http);
530
+ }
531
+ /**
532
+ * Get the profile of the currently authenticated user.
533
+ * The response includes context from the JWT (org, service).
534
+ *
535
+ * @returns User profile
536
+ *
537
+ * @example
538
+ * ```typescript
539
+ * const profile = await sso.user.getProfile();
540
+ * console.log(profile.email, profile.org, profile.service);
541
+ * ```
542
+ */
543
+ async getProfile() {
544
+ const response = await this.http.get("/api/user");
545
+ return response.data;
546
+ }
547
+ /**
548
+ * Update the authenticated user's profile.
549
+ *
550
+ * @param payload Update payload
551
+ * @returns Updated user profile
552
+ *
553
+ * @example
554
+ * ```typescript
555
+ * const updated = await sso.user.updateProfile({
556
+ * email: 'newemail@example.com'
557
+ * });
558
+ * ```
559
+ */
560
+ async updateProfile(payload) {
561
+ const response = await this.http.patch("/api/user", payload);
562
+ return response.data;
563
+ }
564
+ /**
565
+ * Get the current user's subscription details for the service in their JWT.
566
+ *
567
+ * @returns Subscription details
568
+ *
569
+ * @example
570
+ * ```typescript
571
+ * const subscription = await sso.user.getSubscription();
572
+ * console.log(subscription.plan, subscription.features);
573
+ * ```
574
+ */
575
+ async getSubscription() {
576
+ const response = await this.http.get("/api/subscription");
577
+ return response.data;
578
+ }
579
+ };
580
+
581
+ // src/modules/organizations.ts
582
+ var OrganizationsModule = class {
583
+ constructor(http) {
584
+ this.http = http;
585
+ /**
586
+ * Member management methods
587
+ */
588
+ this.members = {
589
+ /**
590
+ * List all members of an organization.
591
+ *
592
+ * @param orgSlug Organization slug
593
+ * @returns Member list response with pagination metadata
594
+ *
595
+ * @example
596
+ * ```typescript
597
+ * const result = await sso.organizations.members.list('acme-corp');
598
+ * console.log(`Total members: ${result.total}`);
599
+ * result.members.forEach(m => console.log(m.email, m.role));
600
+ * ```
601
+ */
602
+ list: async (orgSlug) => {
603
+ const response = await this.http.get(
604
+ `/api/organizations/${orgSlug}/members`
605
+ );
606
+ return response.data;
607
+ },
608
+ /**
609
+ * Update a member's role.
610
+ * Requires 'owner' role.
611
+ *
612
+ * @param orgSlug Organization slug
613
+ * @param userId User ID to update
614
+ * @param payload Role update payload
615
+ * @returns Updated member details
616
+ *
617
+ * @example
618
+ * ```typescript
619
+ * await sso.organizations.members.updateRole('acme-corp', 'user-id', {
620
+ * role: 'admin'
621
+ * });
622
+ * ```
623
+ */
624
+ updateRole: async (orgSlug, userId, payload) => {
625
+ const response = await this.http.patch(
626
+ `/api/organizations/${orgSlug}/members/${userId}`,
627
+ payload
628
+ );
629
+ return response.data;
630
+ },
631
+ /**
632
+ * Remove a member from the organization.
633
+ * Requires 'owner' or 'admin' role.
634
+ *
635
+ * @param orgSlug Organization slug
636
+ * @param userId User ID to remove
637
+ *
638
+ * @example
639
+ * ```typescript
640
+ * await sso.organizations.members.remove('acme-corp', 'user-id');
641
+ * ```
642
+ */
643
+ remove: async (orgSlug, userId) => {
644
+ await this.http.post(`/api/organizations/${orgSlug}/members/${userId}`);
645
+ },
646
+ /**
647
+ * Transfer organization ownership to another member.
648
+ * Requires 'owner' role.
649
+ *
650
+ * @param orgSlug Organization slug
651
+ * @param payload Transfer payload with new owner ID
652
+ *
653
+ * @example
654
+ * ```typescript
655
+ * await sso.organizations.members.transferOwnership('acme-corp', {
656
+ * new_owner_user_id: 'new-owner-id'
657
+ * });
658
+ * ```
659
+ */
660
+ transferOwnership: async (orgSlug, payload) => {
661
+ await this.http.post(`/api/organizations/${orgSlug}/members/transfer-ownership`, payload);
662
+ }
663
+ };
664
+ /**
665
+ * End-user management methods
666
+ * Manage organization's customers (end-users with subscriptions)
667
+ */
668
+ this.endUsers = {
669
+ /**
670
+ * List all end-users for an organization.
671
+ * End-users are customers who have subscriptions to the organization's services.
672
+ *
673
+ * @param orgSlug Organization slug
674
+ * @param params Optional query parameters for pagination
675
+ * @returns Paginated list of end-users with their subscriptions
676
+ *
677
+ * @example
678
+ * ```typescript
679
+ * const endUsers = await sso.organizations.endUsers.list('acme-corp', {
680
+ * page: 1,
681
+ * limit: 20
682
+ * });
683
+ * console.log(`Total end-users: ${endUsers.total}`);
684
+ * ```
685
+ */
686
+ list: async (orgSlug, params) => {
687
+ const response = await this.http.get(
688
+ `/api/organizations/${orgSlug}/users`,
689
+ { params }
690
+ );
691
+ return response.data;
692
+ },
693
+ /**
694
+ * Get detailed information about a specific end-user.
695
+ *
696
+ * @param orgSlug Organization slug
697
+ * @param userId User ID
698
+ * @returns End-user details with subscriptions, identities, and session count
699
+ *
700
+ * @example
701
+ * ```typescript
702
+ * const endUser = await sso.organizations.endUsers.get('acme-corp', 'user-id');
703
+ * console.log(`Active sessions: ${endUser.session_count}`);
704
+ * ```
705
+ */
706
+ get: async (orgSlug, userId) => {
707
+ const response = await this.http.get(
708
+ `/api/organizations/${orgSlug}/users/${userId}`
709
+ );
710
+ return response.data;
711
+ },
712
+ /**
713
+ * Revoke all active sessions for an end-user.
714
+ * Requires admin or owner role.
715
+ * This will force the user to re-authenticate.
716
+ *
717
+ * @param orgSlug Organization slug
718
+ * @param userId User ID
719
+ * @returns Response with number of revoked sessions
720
+ *
721
+ * @example
722
+ * ```typescript
723
+ * const result = await sso.organizations.endUsers.revokeSessions('acme-corp', 'user-id');
724
+ * console.log(`Revoked ${result.revoked_count} sessions`);
725
+ * ```
726
+ */
727
+ revokeSessions: async (orgSlug, userId) => {
728
+ const response = await this.http.delete(
729
+ `/api/organizations/${orgSlug}/users/${userId}/sessions`
730
+ );
731
+ return response.data;
732
+ }
733
+ };
734
+ /**
735
+ * BYOO (Bring Your Own OAuth) credential management
736
+ */
737
+ this.oauthCredentials = {
738
+ /**
739
+ * Set or update custom OAuth credentials for a provider.
740
+ * This enables white-labeled authentication using the organization's
741
+ * own OAuth application.
742
+ * Requires 'owner' or 'admin' role.
743
+ *
744
+ * @param orgSlug Organization slug
745
+ * @param provider OAuth provider
746
+ * @param payload OAuth credentials
747
+ * @returns Created/updated credentials (without secret)
748
+ *
749
+ * @example
750
+ * ```typescript
751
+ * await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
752
+ * client_id: 'Iv1.abc123',
753
+ * client_secret: 'secret-value'
754
+ * });
755
+ * ```
756
+ */
757
+ set: async (orgSlug, provider, payload) => {
758
+ const response = await this.http.post(
759
+ `/api/organizations/${orgSlug}/oauth-credentials/${provider}`,
760
+ payload
761
+ );
762
+ return response.data;
763
+ },
764
+ /**
765
+ * Get the configured OAuth credentials for a provider.
766
+ * The secret is never returned.
767
+ *
768
+ * @param orgSlug Organization slug
769
+ * @param provider OAuth provider
770
+ * @returns OAuth credentials (without secret)
771
+ *
772
+ * @example
773
+ * ```typescript
774
+ * const creds = await sso.organizations.oauthCredentials.get('acme-corp', 'github');
775
+ * console.log(creds.client_id);
776
+ * ```
777
+ */
778
+ get: async (orgSlug, provider) => {
779
+ const response = await this.http.get(
780
+ `/api/organizations/${orgSlug}/oauth-credentials/${provider}`
781
+ );
782
+ return response.data;
783
+ }
784
+ };
785
+ }
786
+ /**
787
+ * Create a new organization (public endpoint).
788
+ * The organization will be created with 'pending' status and requires
789
+ * platform owner approval before becoming active.
790
+ *
791
+ * @param payload Organization creation payload
792
+ * @returns Created organization with owner and membership details
793
+ *
794
+ * @example
795
+ * ```typescript
796
+ * const result = await sso.organizations.createPublic({
797
+ * slug: 'acme-corp',
798
+ * name: 'Acme Corporation',
799
+ * owner_email: 'founder@acme.com'
800
+ * });
801
+ * ```
802
+ */
803
+ async createPublic(payload) {
804
+ const response = await this.http.post("/api/organizations", payload);
805
+ return response.data;
806
+ }
807
+ /**
808
+ * List all organizations the authenticated user is a member of.
809
+ *
810
+ * @param params Optional query parameters for filtering and pagination
811
+ * @returns Array of organization responses
812
+ *
813
+ * @example
814
+ * ```typescript
815
+ * const orgs = await sso.organizations.list({
816
+ * status: 'active',
817
+ * page: 1,
818
+ * limit: 20
819
+ * });
820
+ * ```
821
+ */
822
+ async list(params) {
823
+ const response = await this.http.get("/api/organizations", { params });
824
+ return response.data;
825
+ }
826
+ /**
827
+ * Get detailed information for a specific organization.
828
+ *
829
+ * @param orgSlug Organization slug
830
+ * @returns Organization details
831
+ *
832
+ * @example
833
+ * ```typescript
834
+ * const org = await sso.organizations.get('acme-corp');
835
+ * console.log(org.organization.name, org.membership_count);
836
+ * ```
837
+ */
838
+ async get(orgSlug) {
839
+ const response = await this.http.get(`/api/organizations/${orgSlug}`);
840
+ return response.data;
841
+ }
842
+ /**
843
+ * Update organization details.
844
+ * Requires 'owner' or 'admin' role.
845
+ *
846
+ * @param orgSlug Organization slug
847
+ * @param payload Update payload
848
+ * @returns Updated organization details
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * const updated = await sso.organizations.update('acme-corp', {
853
+ * name: 'Acme Corporation Inc.',
854
+ * max_services: 20
855
+ * });
856
+ * ```
857
+ */
858
+ async update(orgSlug, payload) {
859
+ const response = await this.http.patch(
860
+ `/api/organizations/${orgSlug}`,
861
+ payload
862
+ );
863
+ return response.data;
864
+ }
865
+ };
866
+
867
+ // src/modules/services.ts
868
+ var ServicesModule = class {
869
+ constructor(http) {
870
+ this.http = http;
871
+ /**
872
+ * Plan management methods
873
+ */
874
+ this.plans = {
875
+ /**
876
+ * Create a new subscription plan for a service.
877
+ * Requires 'owner' or 'admin' role.
878
+ *
879
+ * @param orgSlug Organization slug
880
+ * @param serviceSlug Service slug
881
+ * @param payload Plan creation payload
882
+ * @returns Created plan
883
+ *
884
+ * @example
885
+ * ```typescript
886
+ * const plan = await sso.services.plans.create('acme-corp', 'main-app', {
887
+ * name: 'pro',
888
+ * description: 'Pro tier with advanced features',
889
+ * price_monthly: 29.99,
890
+ * features: ['api-access', 'advanced-analytics', 'priority-support']
891
+ * });
892
+ * ```
893
+ */
894
+ create: async (orgSlug, serviceSlug, payload) => {
895
+ const response = await this.http.post(
896
+ `/api/organizations/${orgSlug}/services/${serviceSlug}/plans`,
897
+ payload
898
+ );
899
+ return response.data;
900
+ },
901
+ /**
902
+ * List all plans for a service.
903
+ *
904
+ * @param orgSlug Organization slug
905
+ * @param serviceSlug Service slug
906
+ * @returns Array of plans
907
+ *
908
+ * @example
909
+ * ```typescript
910
+ * const plans = await sso.services.plans.list('acme-corp', 'main-app');
911
+ * plans.forEach(plan => console.log(plan.name, plan.price_monthly));
912
+ * ```
913
+ */
914
+ list: async (orgSlug, serviceSlug) => {
915
+ const response = await this.http.get(
916
+ `/api/organizations/${orgSlug}/services/${serviceSlug}/plans`
917
+ );
918
+ return response.data;
919
+ }
920
+ };
921
+ }
922
+ /**
923
+ * Create a new service for an organization.
924
+ * Requires 'owner' or 'admin' role.
925
+ *
926
+ * @param orgSlug Organization slug
927
+ * @param payload Service creation payload
928
+ * @returns Created service with details
929
+ *
930
+ * @example
931
+ * ```typescript
932
+ * const result = await sso.services.create('acme-corp', {
933
+ * slug: 'main-app',
934
+ * name: 'Main Application',
935
+ * service_type: 'web',
936
+ * github_scopes: ['user:email', 'read:org'],
937
+ * redirect_uris: ['https://app.acme.com/callback']
938
+ * });
939
+ * console.log(result.service.client_id);
940
+ * ```
941
+ */
942
+ async create(orgSlug, payload) {
943
+ const response = await this.http.post(
944
+ `/api/organizations/${orgSlug}/services`,
945
+ payload
946
+ );
947
+ return response.data;
948
+ }
949
+ /**
950
+ * List all services for an organization.
951
+ *
952
+ * @param orgSlug Organization slug
953
+ * @returns Service list response with usage metadata
954
+ *
955
+ * @example
956
+ * ```typescript
957
+ * const result = await sso.services.list('acme-corp');
958
+ * console.log(`Using ${result.usage.current_services} of ${result.usage.max_services} services`);
959
+ * result.services.forEach(svc => console.log(svc.name, svc.client_id));
960
+ * ```
961
+ */
962
+ async list(orgSlug) {
963
+ const response = await this.http.get(`/api/organizations/${orgSlug}/services`);
964
+ return response.data;
965
+ }
966
+ /**
967
+ * Get detailed information for a specific service.
968
+ *
969
+ * @param orgSlug Organization slug
970
+ * @param serviceSlug Service slug
971
+ * @returns Service with provider grants and plans
972
+ *
973
+ * @example
974
+ * ```typescript
975
+ * const service = await sso.services.get('acme-corp', 'main-app');
976
+ * console.log(service.service.redirect_uris);
977
+ * console.log(service.plans);
978
+ * ```
979
+ */
980
+ async get(orgSlug, serviceSlug) {
981
+ const response = await this.http.get(
982
+ `/api/organizations/${orgSlug}/services/${serviceSlug}`
983
+ );
984
+ return response.data;
985
+ }
986
+ /**
987
+ * Update service configuration.
988
+ * Requires 'owner' or 'admin' role.
989
+ *
990
+ * @param orgSlug Organization slug
991
+ * @param serviceSlug Service slug
992
+ * @param payload Update payload
993
+ * @returns Updated service
994
+ *
995
+ * @example
996
+ * ```typescript
997
+ * const updated = await sso.services.update('acme-corp', 'main-app', {
998
+ * name: 'Main Application v2',
999
+ * redirect_uris: ['https://app.acme.com/callback', 'https://app.acme.com/oauth']
1000
+ * });
1001
+ * ```
1002
+ */
1003
+ async update(orgSlug, serviceSlug, payload) {
1004
+ const response = await this.http.patch(
1005
+ `/api/organizations/${orgSlug}/services/${serviceSlug}`,
1006
+ payload
1007
+ );
1008
+ return response.data;
1009
+ }
1010
+ /**
1011
+ * Delete a service.
1012
+ * Requires 'owner' role.
1013
+ *
1014
+ * @param orgSlug Organization slug
1015
+ * @param serviceSlug Service slug
1016
+ *
1017
+ * @example
1018
+ * ```typescript
1019
+ * await sso.services.delete('acme-corp', 'old-app');
1020
+ * ```
1021
+ */
1022
+ async delete(orgSlug, serviceSlug) {
1023
+ await this.http.delete(`/api/organizations/${orgSlug}/services/${serviceSlug}`);
1024
+ }
1025
+ };
1026
+
1027
+ // src/modules/invitations.ts
1028
+ var InvitationsModule = class {
1029
+ constructor(http) {
1030
+ this.http = http;
1031
+ }
1032
+ /**
1033
+ * Create and send an invitation to join an organization.
1034
+ * Requires 'owner' or 'admin' role.
1035
+ *
1036
+ * @param orgSlug Organization slug
1037
+ * @param payload Invitation payload with email and role
1038
+ * @returns Created invitation
1039
+ *
1040
+ * @example
1041
+ * ```typescript
1042
+ * const invitation = await sso.invitations.create('acme-corp', {
1043
+ * invitee_email: 'newuser@example.com',
1044
+ * role: 'member'
1045
+ * });
1046
+ * ```
1047
+ */
1048
+ async create(orgSlug, payload) {
1049
+ const response = await this.http.post(
1050
+ `/api/organizations/${orgSlug}/invitations`,
1051
+ payload
1052
+ );
1053
+ return response.data;
1054
+ }
1055
+ /**
1056
+ * List all invitations for an organization.
1057
+ * Requires 'owner' or 'admin' role.
1058
+ *
1059
+ * @param orgSlug Organization slug
1060
+ * @returns Array of invitations
1061
+ *
1062
+ * @example
1063
+ * ```typescript
1064
+ * const invitations = await sso.invitations.listForOrg('acme-corp');
1065
+ * invitations.forEach(inv => console.log(inv.invitee_email, inv.status));
1066
+ * ```
1067
+ */
1068
+ async listForOrg(orgSlug) {
1069
+ const response = await this.http.get(
1070
+ `/api/organizations/${orgSlug}/invitations`
1071
+ );
1072
+ return response.data;
1073
+ }
1074
+ /**
1075
+ * Cancel a pending invitation.
1076
+ * Requires 'owner' or 'admin' role.
1077
+ *
1078
+ * @param orgSlug Organization slug
1079
+ * @param invitationId Invitation ID to cancel
1080
+ *
1081
+ * @example
1082
+ * ```typescript
1083
+ * await sso.invitations.cancel('acme-corp', 'invitation-id');
1084
+ * ```
1085
+ */
1086
+ async cancel(orgSlug, invitationId) {
1087
+ await this.http.post(`/api/organizations/${orgSlug}/invitations/${invitationId}`);
1088
+ }
1089
+ /**
1090
+ * List invitations received by the current authenticated user.
1091
+ *
1092
+ * @returns Array of invitations with organization details
1093
+ *
1094
+ * @example
1095
+ * ```typescript
1096
+ * const myInvitations = await sso.invitations.listForUser();
1097
+ * myInvitations.forEach(inv => {
1098
+ * console.log(`Invited to ${inv.organization_name} as ${inv.role}`);
1099
+ * });
1100
+ * ```
1101
+ */
1102
+ async listForUser() {
1103
+ const response = await this.http.get("/api/invitations");
1104
+ return response.data;
1105
+ }
1106
+ /**
1107
+ * Accept an invitation using its token.
1108
+ *
1109
+ * @param token Invitation token
1110
+ *
1111
+ * @example
1112
+ * ```typescript
1113
+ * await sso.invitations.accept('invitation-token-from-email');
1114
+ * ```
1115
+ */
1116
+ async accept(token) {
1117
+ const payload = { token };
1118
+ await this.http.post("/api/invitations/accept", payload);
1119
+ }
1120
+ /**
1121
+ * Decline an invitation using its token.
1122
+ *
1123
+ * @param token Invitation token
1124
+ *
1125
+ * @example
1126
+ * ```typescript
1127
+ * await sso.invitations.decline('invitation-token-from-email');
1128
+ * ```
1129
+ */
1130
+ async decline(token) {
1131
+ const payload = { token };
1132
+ await this.http.post("/api/invitations/decline", payload);
1133
+ }
1134
+ };
1135
+
1136
+ // src/modules/platform.ts
1137
+ var PlatformModule = class {
1138
+ constructor(http) {
1139
+ this.http = http;
1140
+ /**
1141
+ * Organization management for platform owners
1142
+ */
1143
+ this.organizations = {
1144
+ /**
1145
+ * List all organizations on the platform with optional filters.
1146
+ *
1147
+ * @param params Optional query parameters for filtering
1148
+ * @returns Platform organizations list with pagination info
1149
+ *
1150
+ * @example
1151
+ * ```typescript
1152
+ * const result = await sso.platform.organizations.list({
1153
+ * status: 'pending',
1154
+ * page: 1,
1155
+ * limit: 50
1156
+ * });
1157
+ * console.log(result.total, result.organizations);
1158
+ * ```
1159
+ */
1160
+ list: async (params) => {
1161
+ const response = await this.http.get(
1162
+ "/api/platform/organizations",
1163
+ { params }
1164
+ );
1165
+ return response.data;
1166
+ },
1167
+ /**
1168
+ * Approve a pending organization and assign it a tier.
1169
+ *
1170
+ * @param orgId Organization ID
1171
+ * @param payload Approval payload with tier assignment
1172
+ * @returns Approved organization
1173
+ *
1174
+ * @example
1175
+ * ```typescript
1176
+ * const approved = await sso.platform.organizations.approve('org-id', {
1177
+ * tier_id: 'tier-starter'
1178
+ * });
1179
+ * ```
1180
+ */
1181
+ approve: async (orgId, payload) => {
1182
+ const response = await this.http.post(
1183
+ `/api/platform/organizations/${orgId}/approve`,
1184
+ payload
1185
+ );
1186
+ return response.data;
1187
+ },
1188
+ /**
1189
+ * Reject a pending organization with a reason.
1190
+ *
1191
+ * @param orgId Organization ID
1192
+ * @param payload Rejection payload with reason
1193
+ * @returns Rejected organization
1194
+ *
1195
+ * @example
1196
+ * ```typescript
1197
+ * await sso.platform.organizations.reject('org-id', {
1198
+ * reason: 'Does not meet platform requirements'
1199
+ * });
1200
+ * ```
1201
+ */
1202
+ reject: async (orgId, payload) => {
1203
+ const response = await this.http.post(
1204
+ `/api/platform/organizations/${orgId}/reject`,
1205
+ payload
1206
+ );
1207
+ return response.data;
1208
+ },
1209
+ /**
1210
+ * Suspend an active organization.
1211
+ *
1212
+ * @param orgId Organization ID
1213
+ * @returns Suspended organization
1214
+ *
1215
+ * @example
1216
+ * ```typescript
1217
+ * await sso.platform.organizations.suspend('org-id');
1218
+ * ```
1219
+ */
1220
+ suspend: async (orgId) => {
1221
+ const response = await this.http.post(
1222
+ `/api/platform/organizations/${orgId}/suspend`
1223
+ );
1224
+ return response.data;
1225
+ },
1226
+ /**
1227
+ * Re-activate a suspended organization.
1228
+ *
1229
+ * @param orgId Organization ID
1230
+ * @returns Activated organization
1231
+ *
1232
+ * @example
1233
+ * ```typescript
1234
+ * await sso.platform.organizations.activate('org-id');
1235
+ * ```
1236
+ */
1237
+ activate: async (orgId) => {
1238
+ const response = await this.http.post(
1239
+ `/api/platform/organizations/${orgId}/activate`
1240
+ );
1241
+ return response.data;
1242
+ },
1243
+ /**
1244
+ * Update an organization's tier and resource limits.
1245
+ *
1246
+ * @param orgId Organization ID
1247
+ * @param payload Tier update payload
1248
+ * @returns Updated organization
1249
+ *
1250
+ * @example
1251
+ * ```typescript
1252
+ * await sso.platform.organizations.updateTier('org-id', {
1253
+ * tier_id: 'tier-pro',
1254
+ * max_services: 20,
1255
+ * max_users: 100
1256
+ * });
1257
+ * ```
1258
+ */
1259
+ updateTier: async (orgId, payload) => {
1260
+ const response = await this.http.patch(
1261
+ `/api/platform/organizations/${orgId}/tier`,
1262
+ payload
1263
+ );
1264
+ return response.data;
1265
+ }
1266
+ };
1267
+ }
1268
+ /**
1269
+ * List all available organization tiers.
1270
+ *
1271
+ * @returns Array of organization tiers
1272
+ *
1273
+ * @example
1274
+ * ```typescript
1275
+ * const tiers = await sso.platform.getTiers();
1276
+ * console.log(tiers); // [{ id: 'tier_free', display_name: 'Free Tier', ... }]
1277
+ * ```
1278
+ */
1279
+ async getTiers() {
1280
+ const response = await this.http.get("/api/platform/tiers");
1281
+ return response.data;
1282
+ }
1283
+ /**
1284
+ * Promote an existing user to platform owner.
1285
+ *
1286
+ * @param payload Promotion payload with user ID
1287
+ *
1288
+ * @example
1289
+ * ```typescript
1290
+ * await sso.platform.promoteOwner({
1291
+ * user_id: 'user-uuid-here'
1292
+ * });
1293
+ * ```
1294
+ */
1295
+ async promoteOwner(payload) {
1296
+ await this.http.post("/api/platform/owners", payload);
1297
+ }
1298
+ /**
1299
+ * Demote a platform owner to regular user.
1300
+ *
1301
+ * @param userId The ID of the user to demote
1302
+ *
1303
+ * @example
1304
+ * ```typescript
1305
+ * await sso.platform.demoteOwner('user-uuid-here');
1306
+ * ```
1307
+ */
1308
+ async demoteOwner(userId) {
1309
+ await this.http.delete(`/api/platform/owners/${userId}`);
1310
+ }
1311
+ /**
1312
+ * Retrieve the platform-wide audit log with optional filters.
1313
+ *
1314
+ * @param params Optional query parameters for filtering
1315
+ * @returns Array of audit log entries
1316
+ *
1317
+ * @example
1318
+ * ```typescript
1319
+ * const logs = await sso.platform.getAuditLog({
1320
+ * action: 'organization.approved',
1321
+ * start_date: '2024-01-01',
1322
+ * limit: 100
1323
+ * });
1324
+ * ```
1325
+ */
1326
+ async getAuditLog(params) {
1327
+ const response = await this.http.get("/api/platform/audit-log", { params });
1328
+ return response.data;
1329
+ }
1330
+ };
1331
+
1332
+ // src/client.ts
1333
+ var SsoClient = class {
1334
+ constructor(options) {
1335
+ this.http = createHttpAgent(options.baseURL);
1336
+ if (options.token) {
1337
+ this.setAuthToken(options.token);
1338
+ }
1339
+ this.analytics = new AnalyticsModule(this.http);
1340
+ this.auth = new AuthModule(this.http);
1341
+ this.user = new UserModule(this.http);
1342
+ this.organizations = new OrganizationsModule(this.http);
1343
+ this.services = new ServicesModule(this.http);
1344
+ this.invitations = new InvitationsModule(this.http);
1345
+ this.platform = new PlatformModule(this.http);
1346
+ }
1347
+ /**
1348
+ * Sets the JWT for all subsequent authenticated requests.
1349
+ * Pass null to clear the token.
1350
+ *
1351
+ * @param token The JWT string, or null to clear
1352
+ *
1353
+ * @example
1354
+ * ```typescript
1355
+ * // Set token
1356
+ * sso.setAuthToken(jwt);
1357
+ *
1358
+ * // Clear token
1359
+ * sso.setAuthToken(null);
1360
+ * ```
1361
+ */
1362
+ setAuthToken(token) {
1363
+ if (token) {
1364
+ this.http.defaults.headers.common["Authorization"] = `Bearer ${token}`;
1365
+ } else {
1366
+ delete this.http.defaults.headers.common["Authorization"];
1367
+ }
1368
+ }
1369
+ /**
1370
+ * Gets the current base URL
1371
+ */
1372
+ getBaseURL() {
1373
+ return this.http.defaults.baseURL || "";
1374
+ }
1375
+ };
1376
+ // Annotate the CommonJS export names for ESM import in node:
1377
+ 0 && (module.exports = {
1378
+ AuthModule,
1379
+ InvitationsModule,
1380
+ OrganizationsModule,
1381
+ PlatformModule,
1382
+ ServicesModule,
1383
+ SsoApiError,
1384
+ SsoClient,
1385
+ UserModule
1386
+ });