@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/LICENSE +21 -0
- package/README.md +400 -0
- package/dist/index.d.mts +1759 -0
- package/dist/index.d.ts +1759 -0
- package/dist/index.js +1386 -0
- package/dist/index.mjs +1352 -0
- package/package.json +47 -0
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
|
+
};
|