@drmhse/sso-sdk 0.1.1 → 0.2.1
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/README.md +86 -303
- package/dist/index.d.mts +222 -1
- package/dist/index.d.ts +222 -1
- package/dist/index.js +172 -0
- package/dist/index.mjs +172 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,14 +4,12 @@ A zero-dependency, strongly-typed TypeScript SDK for interacting with the multi-
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
- **Comprehensive Documentation**: Full TSDoc comments for excellent IDE support
|
|
14
|
-
- **Modern**: Supports Node.js 18+ and all modern browsers
|
|
7
|
+
- **Zero Dependencies**: Built on native `fetch` API - no external dependencies
|
|
8
|
+
- **Framework Agnostic**: Pure TypeScript - works in any JavaScript environment
|
|
9
|
+
- **Strongly Typed**: Complete TypeScript definitions for all API endpoints
|
|
10
|
+
- **Stateless Design**: No internal state management - integrates with any state solution
|
|
11
|
+
- **Predictable Error Handling**: Custom `SsoApiError` class with structured error information
|
|
12
|
+
- **Modern**: Supports Node.js 18+ and all modern browsers
|
|
15
13
|
|
|
16
14
|
## Installation
|
|
17
15
|
|
|
@@ -22,12 +20,12 @@ npm install @drmhse/sso-sdk
|
|
|
22
20
|
## Quick Start
|
|
23
21
|
|
|
24
22
|
```typescript
|
|
25
|
-
import { SsoClient } from '@drmhse/sso-sdk';
|
|
23
|
+
import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
|
|
26
24
|
|
|
27
25
|
// Initialize the client
|
|
28
26
|
const sso = new SsoClient({
|
|
29
27
|
baseURL: 'https://sso.example.com',
|
|
30
|
-
token: localStorage.getItem('
|
|
28
|
+
token: localStorage.getItem('sso_token') // Optional initial token
|
|
31
29
|
});
|
|
32
30
|
|
|
33
31
|
// Use the SDK
|
|
@@ -53,7 +51,7 @@ async function example() {
|
|
|
53
51
|
### End-User OAuth Login
|
|
54
52
|
|
|
55
53
|
```typescript
|
|
56
|
-
// Redirect user to OAuth provider
|
|
54
|
+
// 1. Redirect user to OAuth provider
|
|
57
55
|
const loginUrl = sso.auth.getLoginUrl('github', {
|
|
58
56
|
org: 'acme-corp',
|
|
59
57
|
service: 'main-app',
|
|
@@ -61,11 +59,15 @@ const loginUrl = sso.auth.getLoginUrl('github', {
|
|
|
61
59
|
});
|
|
62
60
|
window.location.href = loginUrl;
|
|
63
61
|
|
|
64
|
-
// In your callback handler
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
// 2. In your callback handler, extract both tokens from the URL
|
|
63
|
+
const params = new URLSearchParams(window.location.search);
|
|
64
|
+
const accessToken = params.get('access_token');
|
|
65
|
+
const refreshToken = params.get('refresh_token');
|
|
66
|
+
|
|
67
|
+
if (accessToken && refreshToken) {
|
|
68
|
+
sso.setAuthToken(accessToken);
|
|
69
|
+
localStorage.setItem('sso_token', accessToken);
|
|
70
|
+
localStorage.setItem('sso_refresh_token', refreshToken);
|
|
69
71
|
}
|
|
70
72
|
```
|
|
71
73
|
|
|
@@ -73,43 +75,65 @@ if (token) {
|
|
|
73
75
|
|
|
74
76
|
```typescript
|
|
75
77
|
const adminUrl = sso.auth.getAdminLoginUrl('github', {
|
|
76
|
-
org_slug: 'acme-corp' // Optional
|
|
78
|
+
org_slug: 'acme-corp' // Optional: directs user to a specific org dashboard after login
|
|
77
79
|
});
|
|
78
80
|
window.location.href = adminUrl;
|
|
79
81
|
```
|
|
80
82
|
|
|
81
83
|
### Device Flow (for CLIs)
|
|
82
84
|
|
|
85
|
+
This flow involves both the CLI and a web browser.
|
|
86
|
+
|
|
83
87
|
```typescript
|
|
84
|
-
//
|
|
88
|
+
// --- In your CLI Application ---
|
|
89
|
+
|
|
90
|
+
// 1. Request device code
|
|
85
91
|
const deviceAuth = await sso.auth.deviceCode.request({
|
|
86
92
|
client_id: 'cli-client-id',
|
|
87
93
|
org: 'acme-corp',
|
|
88
94
|
service: 'acme-cli'
|
|
89
95
|
});
|
|
90
96
|
|
|
91
|
-
console.log(`Visit ${deviceAuth.verification_uri}`);
|
|
97
|
+
console.log(`Visit: ${deviceAuth.verification_uri}`);
|
|
92
98
|
console.log(`Enter code: ${deviceAuth.user_code}`);
|
|
93
99
|
|
|
94
|
-
//
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
100
|
+
// 3. Poll for the token
|
|
101
|
+
const pollForToken = async () => {
|
|
102
|
+
// Polling logic...
|
|
103
|
+
};
|
|
104
|
+
pollForToken();
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
// --- In your Web Application at /activate ---
|
|
108
|
+
|
|
109
|
+
// 2. After user enters the code, verify it to get context
|
|
110
|
+
const context = await sso.auth.deviceCode.verify(userEnteredCode);
|
|
111
|
+
|
|
112
|
+
// Redirect user to the appropriate login flow with the user_code
|
|
113
|
+
const loginUrl = sso.auth.getLoginUrl('github', {
|
|
114
|
+
org: context.org_slug,
|
|
115
|
+
service: context.service_slug,
|
|
116
|
+
user_code: userEnteredCode,
|
|
117
|
+
});
|
|
118
|
+
window.location.href = loginUrl; // User logs in, authorizing the device
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Refreshing Tokens
|
|
122
|
+
|
|
123
|
+
Renew an expired access token using a refresh token. This uses token rotation for enhanced security.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
try {
|
|
127
|
+
const tokens = await sso.auth.refreshToken(storedRefreshToken);
|
|
128
|
+
|
|
129
|
+
// Update tokens in your application state and storage
|
|
130
|
+
sso.setAuthToken(tokens.access_token);
|
|
131
|
+
localStorage.setItem('sso_token', tokens.access_token);
|
|
132
|
+
localStorage.setItem('sso_refresh_token', tokens.refresh_token);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
// Refresh failed, user needs to log in again
|
|
135
|
+
console.error("Token refresh failed:", error);
|
|
136
|
+
}
|
|
113
137
|
```
|
|
114
138
|
|
|
115
139
|
### Logout
|
|
@@ -117,7 +141,8 @@ const pollInterval = setInterval(async () => {
|
|
|
117
141
|
```typescript
|
|
118
142
|
await sso.auth.logout();
|
|
119
143
|
sso.setAuthToken(null);
|
|
120
|
-
localStorage.removeItem('
|
|
144
|
+
localStorage.removeItem('sso_token');
|
|
145
|
+
localStorage.removeItem('sso_refresh_token');
|
|
121
146
|
```
|
|
122
147
|
|
|
123
148
|
## API Reference
|
|
@@ -132,17 +157,6 @@ const trends = await sso.analytics.getLoginTrends('acme-corp', {
|
|
|
132
157
|
start_date: '2025-01-01',
|
|
133
158
|
end_date: '2025-01-31'
|
|
134
159
|
});
|
|
135
|
-
|
|
136
|
-
// Get logins grouped by service
|
|
137
|
-
const byService = await sso.analytics.getLoginsByService('acme-corp');
|
|
138
|
-
|
|
139
|
-
// Get logins grouped by OAuth provider
|
|
140
|
-
const byProvider = await sso.analytics.getLoginsByProvider('acme-corp');
|
|
141
|
-
|
|
142
|
-
// Get recent login events
|
|
143
|
-
const recent = await sso.analytics.getRecentLogins('acme-corp', {
|
|
144
|
-
limit: 10
|
|
145
|
-
});
|
|
146
160
|
```
|
|
147
161
|
|
|
148
162
|
### Organizations
|
|
@@ -155,32 +169,11 @@ const org = await sso.organizations.createPublic({
|
|
|
155
169
|
owner_email: 'founder@acme.com'
|
|
156
170
|
});
|
|
157
171
|
|
|
158
|
-
// List user's organizations
|
|
159
|
-
const orgs = await sso.organizations.list({ status: 'active' });
|
|
160
|
-
|
|
161
|
-
// Get organization details
|
|
162
|
-
const details = await sso.organizations.get('acme-corp');
|
|
163
|
-
|
|
164
|
-
// Update organization
|
|
165
|
-
await sso.organizations.update('acme-corp', {
|
|
166
|
-
name: 'Acme Corp Inc.'
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Manage members
|
|
170
|
-
const members = await sso.organizations.members.list('acme-corp');
|
|
171
|
-
await sso.organizations.members.updateRole('acme-corp', 'user-id', {
|
|
172
|
-
role: 'admin'
|
|
173
|
-
});
|
|
174
|
-
await sso.organizations.members.remove('acme-corp', 'user-id');
|
|
175
|
-
|
|
176
172
|
// BYOO: Set custom OAuth credentials
|
|
177
173
|
await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
|
|
178
174
|
client_id: 'Iv1.abc123',
|
|
179
175
|
client_secret: 'secret-value'
|
|
180
176
|
});
|
|
181
|
-
|
|
182
|
-
// Get configured OAuth credentials
|
|
183
|
-
const creds = await sso.organizations.oauthCredentials.get('acme-corp', 'github');
|
|
184
177
|
```
|
|
185
178
|
|
|
186
179
|
### End-User Management
|
|
@@ -194,124 +187,44 @@ const endUsers = await sso.organizations.endUsers.list('acme-corp', {
|
|
|
194
187
|
limit: 20
|
|
195
188
|
});
|
|
196
189
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Revoke all active sessions for an end-user
|
|
201
|
-
const result = await sso.organizations.endUsers.revokeSessions('acme-corp', 'user-id');
|
|
190
|
+
// Revoke all active sessions for a specific end-user
|
|
191
|
+
await sso.organizations.endUsers.revokeSessions('acme-corp', 'user-id-123');
|
|
202
192
|
```
|
|
203
193
|
|
|
204
|
-
### Services
|
|
194
|
+
### Services & Plans
|
|
205
195
|
|
|
206
196
|
```typescript
|
|
207
|
-
// Create
|
|
197
|
+
// Create a service
|
|
208
198
|
const result = await sso.services.create('acme-corp', {
|
|
209
199
|
slug: 'main-app',
|
|
210
200
|
name: 'Main Application',
|
|
211
201
|
service_type: 'web',
|
|
212
|
-
github_scopes: ['user:email', 'read:org'],
|
|
213
|
-
microsoft_scopes: ['User.Read', 'email'],
|
|
214
|
-
google_scopes: ['openid', 'email', 'profile'],
|
|
215
202
|
redirect_uris: ['https://app.acme.com/callback']
|
|
216
203
|
});
|
|
217
|
-
console.log(result.service.client_id);
|
|
218
|
-
console.log(result.usage.current_services);
|
|
219
|
-
|
|
220
|
-
// List services (returns services with usage metadata)
|
|
221
|
-
const result = await sso.services.list('acme-corp');
|
|
222
|
-
console.log(`Using ${result.usage.current_services} of ${result.usage.max_services} services`);
|
|
223
|
-
result.services.forEach(svc => console.log(svc.name, svc.client_id));
|
|
224
|
-
|
|
225
|
-
// Get service details (includes provider grants and plans)
|
|
226
|
-
const service = await sso.services.get('acme-corp', 'main-app');
|
|
227
|
-
console.log(service.service.redirect_uris);
|
|
228
|
-
console.log(service.plans);
|
|
229
|
-
|
|
230
|
-
// Update service
|
|
231
|
-
const updated = await sso.services.update('acme-corp', 'main-app', {
|
|
232
|
-
name: 'Main Application v2',
|
|
233
|
-
github_scopes: ['user:email', 'read:org', 'repo'],
|
|
234
|
-
microsoft_scopes: ['User.Read', 'email', 'Mail.Read'],
|
|
235
|
-
google_scopes: ['openid', 'email', 'profile', 'drive.readonly'],
|
|
236
|
-
redirect_uris: ['https://app.acme.com/callback', 'https://app.acme.com/oauth']
|
|
237
|
-
});
|
|
238
204
|
|
|
239
|
-
//
|
|
240
|
-
await sso.services.
|
|
241
|
-
|
|
242
|
-
// Manage plans
|
|
243
|
-
const plan = await sso.services.plans.create('acme-corp', 'main-app', {
|
|
205
|
+
// Create a subscription plan for that service
|
|
206
|
+
await sso.services.plans.create('acme-corp', 'main-app', {
|
|
244
207
|
name: 'pro',
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
features: ['api-access', '
|
|
208
|
+
price_cents: 2999, // Note: price is in cents
|
|
209
|
+
currency: 'usd',
|
|
210
|
+
features: ['api-access', 'priority-support']
|
|
248
211
|
});
|
|
249
|
-
|
|
250
|
-
// List all plans for a service
|
|
251
|
-
const plans = await sso.services.plans.list('acme-corp', 'main-app');
|
|
252
|
-
plans.forEach(plan => console.log(plan.name, plan.price_monthly));
|
|
253
212
|
```
|
|
254
213
|
|
|
255
|
-
###
|
|
214
|
+
### User Profile & Identities
|
|
256
215
|
|
|
257
|
-
|
|
258
|
-
// Send invitation
|
|
259
|
-
const invitation = await sso.invitations.create('acme-corp', {
|
|
260
|
-
invitee_email: 'newuser@example.com',
|
|
261
|
-
role: 'member'
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// List organization's invitations
|
|
265
|
-
const orgInvites = await sso.invitations.listForOrg('acme-corp');
|
|
266
|
-
|
|
267
|
-
// List user's invitations
|
|
268
|
-
const myInvites = await sso.invitations.listForUser();
|
|
269
|
-
|
|
270
|
-
// Accept invitation
|
|
271
|
-
await sso.invitations.accept('invitation-token');
|
|
272
|
-
|
|
273
|
-
// Decline invitation
|
|
274
|
-
await sso.invitations.decline('invitation-token');
|
|
275
|
-
|
|
276
|
-
// Cancel invitation
|
|
277
|
-
await sso.invitations.cancel('acme-corp', 'invitation-id');
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### User Profile
|
|
216
|
+
Manage the authenticated user's own profile and linked social accounts.
|
|
281
217
|
|
|
282
218
|
```typescript
|
|
283
219
|
// Get profile
|
|
284
220
|
const profile = await sso.user.getProfile();
|
|
285
221
|
|
|
286
|
-
// Update profile
|
|
287
|
-
await sso.user.updateProfile({ email: 'newemail@example.com' });
|
|
288
|
-
|
|
289
|
-
// Get subscription
|
|
290
|
-
const subscription = await sso.user.getSubscription();
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
### Social Account Identities
|
|
294
|
-
|
|
295
|
-
Manage linked social accounts for the authenticated user.
|
|
296
|
-
|
|
297
|
-
```typescript
|
|
298
|
-
// List all linked social accounts
|
|
299
|
-
const identities = await sso.user.identities.list();
|
|
300
|
-
|
|
301
222
|
// Start linking a new social account
|
|
302
|
-
const { authorization_url } = await sso.user.identities.startLink('
|
|
223
|
+
const { authorization_url } = await sso.user.identities.startLink('google');
|
|
303
224
|
window.location.href = authorization_url;
|
|
304
225
|
|
|
305
226
|
// Unlink a social account
|
|
306
|
-
await sso.user.identities.unlink('
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
### Provider Tokens
|
|
310
|
-
|
|
311
|
-
```typescript
|
|
312
|
-
// Get fresh OAuth token for external provider
|
|
313
|
-
const githubToken = await sso.auth.getProviderToken('github');
|
|
314
|
-
// Use githubToken.access_token to make GitHub API calls
|
|
227
|
+
await sso.user.identities.unlink('github');
|
|
315
228
|
```
|
|
316
229
|
|
|
317
230
|
### Platform Administration
|
|
@@ -319,49 +232,19 @@ const githubToken = await sso.auth.getProviderToken('github');
|
|
|
319
232
|
Platform owner methods require a Platform Owner JWT.
|
|
320
233
|
|
|
321
234
|
```typescript
|
|
322
|
-
// List all organizations
|
|
323
|
-
const
|
|
324
|
-
status: 'pending'
|
|
325
|
-
page: 1,
|
|
326
|
-
limit: 50
|
|
235
|
+
// List all organizations awaiting approval
|
|
236
|
+
const pendingOrgs = await sso.platform.organizations.list({
|
|
237
|
+
status: 'pending'
|
|
327
238
|
});
|
|
328
239
|
|
|
329
|
-
// Approve organization
|
|
330
|
-
await sso.platform.organizations.approve('org-id', {
|
|
240
|
+
// Approve an organization
|
|
241
|
+
await sso.platform.organizations.approve('org-id-123', {
|
|
331
242
|
tier_id: 'tier-starter'
|
|
332
243
|
});
|
|
333
244
|
|
|
334
|
-
//
|
|
335
|
-
await sso.platform.
|
|
336
|
-
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
// Suspend/activate
|
|
340
|
-
await sso.platform.organizations.suspend('org-id');
|
|
341
|
-
await sso.platform.organizations.activate('org-id');
|
|
342
|
-
|
|
343
|
-
// Update tier
|
|
344
|
-
await sso.platform.organizations.updateTier('org-id', {
|
|
345
|
-
tier_id: 'tier-pro',
|
|
346
|
-
max_services: 20
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Promote platform owner
|
|
350
|
-
await sso.platform.promoteOwner({
|
|
351
|
-
user_id: 'user-uuid-here'
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
// Demote platform owner to regular user
|
|
355
|
-
await sso.platform.demoteOwner('user-uuid-here');
|
|
356
|
-
|
|
357
|
-
// List available organization tiers
|
|
358
|
-
const tiers = await sso.platform.getTiers();
|
|
359
|
-
|
|
360
|
-
// Get audit log
|
|
361
|
-
const logs = await sso.platform.getAuditLog({
|
|
362
|
-
action: 'organization.approved',
|
|
363
|
-
limit: 100
|
|
364
|
-
});
|
|
245
|
+
// Get platform-wide analytics
|
|
246
|
+
const overview = await sso.platform.analytics.getOverview();
|
|
247
|
+
console.log(`Total Users: ${overview.total_users}`);
|
|
365
248
|
```
|
|
366
249
|
|
|
367
250
|
## Error Handling
|
|
@@ -377,98 +260,17 @@ try {
|
|
|
377
260
|
if (error instanceof SsoApiError) {
|
|
378
261
|
console.error(`Error ${error.statusCode}: ${error.message}`);
|
|
379
262
|
console.error(`Code: ${error.errorCode}`);
|
|
380
|
-
console.error(`Timestamp: ${error.timestamp}`);
|
|
381
263
|
|
|
382
|
-
// Utility methods
|
|
383
264
|
if (error.isAuthError()) {
|
|
384
265
|
// Redirect to login
|
|
385
266
|
}
|
|
386
|
-
if (error.isNotFound()) {
|
|
387
|
-
// Show 404 page
|
|
388
|
-
}
|
|
389
|
-
if (error.is('SERVICE_LIMIT_EXCEEDED')) {
|
|
390
|
-
// Handle specific error
|
|
391
|
-
}
|
|
392
|
-
if (error.isForbidden()) {
|
|
393
|
-
// Handle permission errors
|
|
394
|
-
}
|
|
395
|
-
if (error.isAuthError()) {
|
|
396
|
-
// Handle authentication errors
|
|
397
|
-
}
|
|
398
267
|
}
|
|
399
268
|
}
|
|
400
269
|
```
|
|
401
270
|
|
|
402
|
-
## Framework Integration Examples
|
|
403
|
-
|
|
404
|
-
### Vue 3 + Pinia
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
// stores/auth.ts
|
|
408
|
-
import { defineStore } from 'pinia';
|
|
409
|
-
import { SsoClient } from '@drmhse/sso-sdk';
|
|
410
|
-
|
|
411
|
-
export const useAuthStore = defineStore('auth', {
|
|
412
|
-
state: () => ({
|
|
413
|
-
token: localStorage.getItem('jwt'),
|
|
414
|
-
user: null
|
|
415
|
-
}),
|
|
416
|
-
|
|
417
|
-
actions: {
|
|
418
|
-
async login(token: string) {
|
|
419
|
-
this.token = token;
|
|
420
|
-
localStorage.setItem('jwt', token);
|
|
421
|
-
sso.setAuthToken(token);
|
|
422
|
-
await this.fetchUser();
|
|
423
|
-
},
|
|
424
|
-
|
|
425
|
-
async logout() {
|
|
426
|
-
await sso.auth.logout();
|
|
427
|
-
this.token = null;
|
|
428
|
-
this.user = null;
|
|
429
|
-
localStorage.removeItem('jwt');
|
|
430
|
-
sso.setAuthToken(null);
|
|
431
|
-
},
|
|
432
|
-
|
|
433
|
-
async fetchUser() {
|
|
434
|
-
this.user = await sso.user.getProfile();
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
// Global SSO instance
|
|
440
|
-
export const sso = new SsoClient({
|
|
441
|
-
baseURL: import.meta.env.VITE_SSO_URL,
|
|
442
|
-
token: localStorage.getItem('jwt')
|
|
443
|
-
});
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### React + Context
|
|
447
|
-
|
|
448
|
-
```typescript
|
|
449
|
-
// SsoContext.tsx
|
|
450
|
-
import { createContext, useContext } from 'react';
|
|
451
|
-
import { SsoClient } from '@drmhse/sso-sdk';
|
|
452
|
-
|
|
453
|
-
const sso = new SsoClient({
|
|
454
|
-
baseURL: process.env.REACT_APP_SSO_URL,
|
|
455
|
-
token: localStorage.getItem('jwt')
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
const SsoContext = createContext(sso);
|
|
459
|
-
|
|
460
|
-
export const useSso = () => useContext(SsoContext);
|
|
461
|
-
|
|
462
|
-
export const SsoProvider = ({ children }) => (
|
|
463
|
-
<SsoContext.Provider value={sso}>
|
|
464
|
-
{children}
|
|
465
|
-
</SsoContext.Provider>
|
|
466
|
-
);
|
|
467
|
-
```
|
|
468
|
-
|
|
469
271
|
## TypeScript
|
|
470
272
|
|
|
471
|
-
The SDK is written in TypeScript and includes complete type definitions.
|
|
273
|
+
The SDK is written in TypeScript and includes complete type definitions.
|
|
472
274
|
|
|
473
275
|
```typescript
|
|
474
276
|
import type {
|
|
@@ -476,30 +278,11 @@ import type {
|
|
|
476
278
|
Service,
|
|
477
279
|
User,
|
|
478
280
|
JwtClaims,
|
|
479
|
-
OAuthProvider,
|
|
480
|
-
SsoClientOptions,
|
|
481
281
|
SsoApiError,
|
|
482
|
-
AnalyticsQuery,
|
|
483
|
-
LoginTrendPoint,
|
|
484
|
-
LoginsByService,
|
|
485
|
-
LoginsByProvider,
|
|
486
|
-
RecentLogin,
|
|
487
|
-
Invitation,
|
|
488
|
-
Subscription,
|
|
489
|
-
ProviderToken,
|
|
490
|
-
UserProfile,
|
|
491
|
-
PlatformOrganizationResponse,
|
|
492
|
-
AuditLogEntry,
|
|
493
282
|
// ... and many more types
|
|
494
283
|
} from '@drmhse/sso-sdk';
|
|
495
284
|
```
|
|
496
285
|
|
|
497
|
-
All API responses, request payloads, and configuration options are fully typed for excellent IDE support and compile-time safety.
|
|
498
|
-
|
|
499
286
|
## License
|
|
500
287
|
|
|
501
288
|
MIT
|
|
502
|
-
|
|
503
|
-
## Contributing
|
|
504
|
-
|
|
505
|
-
Contributions are welcome! Please open an issue or pull request.
|