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