@drmhse/sso-sdk 0.5.0 → 0.5.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 +39 -524
- package/package.json +4 -5
package/README.md
CHANGED
|
@@ -1,577 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @drmhse/sso-sdk
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@drmhse/sso-sdk)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Core TypeScript SDK for AuthOS. It handles authentication flows, session persistence, token refresh, and the multi-tenant API surface used by the framework adapters.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Full documentation: [authos.dev/docs/sdk/](https://authos.dev/docs/sdk/)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
AI agent skills: [authos.dev/docs/ai-agent-skills/](https://authos.dev/docs/ai-agent-skills/) and [github.com/CkCreative/authos_skill](https://github.com/CkCreative/authos_skill)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
- **Strongly Typed** - Complete TypeScript definitions
|
|
14
|
-
- **Framework Agnostic** - Works in any JavaScript environment
|
|
15
|
-
- **Automatic Session Management** - Invisible token persistence and auto-refresh
|
|
16
|
-
- **Smart Token Handling** - Auto-inject tokens and handle 401 errors transparently
|
|
17
|
-
- **OAuth 2.0 Flows** - Support for GitHub, Google, Microsoft
|
|
18
|
-
- **Password Authentication** - Native email/password auth with MFA
|
|
19
|
-
- **Device Flow** - RFC 8628 for CLIs and headless apps
|
|
20
|
-
- **Multi-Factor Authentication** - TOTP-based 2FA with backup codes
|
|
21
|
-
- **Organization Management** - Multi-tenant with RBAC
|
|
22
|
-
- **Analytics & Audit Logs** - Track authentication and administrative actions
|
|
23
|
-
- **SAML 2.0** - Act as Identity Provider
|
|
24
|
-
|
|
25
|
-
## Installation
|
|
12
|
+
## Install
|
|
26
13
|
|
|
27
14
|
```bash
|
|
28
15
|
npm install @drmhse/sso-sdk
|
|
29
16
|
```
|
|
30
17
|
|
|
31
|
-
## Quick
|
|
18
|
+
## Quick start
|
|
32
19
|
|
|
33
|
-
```
|
|
20
|
+
```ts
|
|
34
21
|
import { SsoClient } from '@drmhse/sso-sdk';
|
|
35
22
|
|
|
36
|
-
// Initialize the client - automatically loads tokens from localStorage
|
|
37
23
|
const sso = new SsoClient({
|
|
38
|
-
baseURL: 'https://sso.example.com'
|
|
24
|
+
baseURL: 'https://sso.example.com',
|
|
39
25
|
});
|
|
40
26
|
|
|
41
|
-
// Login - session is automatically saved
|
|
42
27
|
await sso.auth.login({
|
|
43
28
|
email: 'user@example.com',
|
|
44
|
-
password: 'SecurePass123!'
|
|
29
|
+
password: 'SecurePass123!',
|
|
30
|
+
org_slug: 'acme-corp',
|
|
31
|
+
service_slug: 'main-app',
|
|
45
32
|
});
|
|
46
33
|
|
|
47
|
-
// Make authenticated requests - tokens auto-injected and auto-refreshed
|
|
48
34
|
const profile = await sso.user.getProfile();
|
|
49
35
|
console.log(profile.email);
|
|
50
|
-
|
|
51
|
-
const orgs = await sso.organizations.list();
|
|
52
|
-
console.log(orgs);
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Understanding Context Modes
|
|
56
|
-
|
|
57
|
-
AuthOS supports **two initialization modes** that determine how authentication is handled:
|
|
58
|
-
|
|
59
|
-
### Platform-Level Mode
|
|
60
|
-
|
|
61
|
-
For platform owners and administrators managing AuthOS itself:
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
const sso = new SsoClient({
|
|
65
|
-
baseURL: 'https://sso.example.com'
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// Platform-level login (email/password only)
|
|
69
|
-
await sso.auth.login({
|
|
70
|
-
email: 'admin@platform.com',
|
|
71
|
-
password: 'SecurePass123!'
|
|
72
|
-
});
|
|
73
|
-
// JWT contains: { is_platform_owner: true, ... }
|
|
74
36
|
```
|
|
75
37
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
### Multi-Tenant Mode
|
|
38
|
+
## Common usage modes
|
|
79
39
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
// Initialize with org and service context
|
|
84
|
-
const sso = new SsoClient({
|
|
85
|
-
baseURL: 'https://sso.example.com'
|
|
86
|
-
});
|
|
40
|
+
### Platform administration
|
|
87
41
|
|
|
88
|
-
|
|
89
|
-
// 1. Which tenant's OAuth credentials (BYOO) to use
|
|
90
|
-
// 2. Which service the identity is attributed to
|
|
91
|
-
const loginUrl = sso.auth.getLoginUrl('github', {
|
|
92
|
-
org: 'acme-corp', // Organization slug
|
|
93
|
-
service: 'main-app', // Service within that org
|
|
94
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
95
|
-
});
|
|
42
|
+
Use only `baseURL` when acting as a platform owner or admin tool:
|
|
96
43
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
email: 'user@example.com',
|
|
100
|
-
password: 'SecurePass123!',
|
|
101
|
-
org_slug: 'acme-corp', // Optional: scopes JWT to this org
|
|
102
|
-
service_slug: 'main-app',
|
|
103
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
104
|
-
});
|
|
105
|
-
// JWT contains: { org: 'acme-corp', ... }
|
|
44
|
+
```ts
|
|
45
|
+
const sso = new SsoClient({ baseURL: 'https://sso.example.com' });
|
|
106
46
|
```
|
|
107
47
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
### Why Context Matters
|
|
111
|
-
|
|
112
|
-
| Context | JWT Claims | OAuth Credentials Used |
|
|
113
|
-
|---------|-----------|----------------------|
|
|
114
|
-
| Platform-level | `is_platform_owner: true` | Platform's `PLATFORM_*` credentials |
|
|
115
|
-
| Multi-tenant | `org: 'slug'`, `service: 'slug'` | Tenant's BYOO credentials (or platform fallback) |
|
|
48
|
+
### Tenant application
|
|
116
49
|
|
|
117
|
-
|
|
118
|
-
- **Identity isolation**: User identities are scoped to specific services
|
|
119
|
-
- **Credential isolation**: Each tenant can use their own OAuth app credentials
|
|
120
|
-
- **Proper attribution**: Login events are tracked per organization/service
|
|
50
|
+
Pass organization and service context when you need hosted auth, BYOO, or service-scoped tokens:
|
|
121
51
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
**Essential Guides:**
|
|
125
|
-
- **[Authentication Flows](https://drmhse.com/docs/sso/sdk/guides/authentication-flows)** - OAuth, Device Flow, and Admin Login patterns
|
|
126
|
-
- **[Password Authentication](https://drmhse.com/docs/sso/sdk/guides/password-authentication)** - Registration, login, and password reset
|
|
127
|
-
- **[MFA Management](https://drmhse.com/docs/sso/sdk/guides/mfa-management)** - TOTP-based 2FA implementation
|
|
128
|
-
- **[Error Handling](https://drmhse.com/docs/sso/sdk/guides/error-handling)** - Best practices for handling API errors
|
|
129
|
-
|
|
130
|
-
## Authentication Examples
|
|
131
|
-
|
|
132
|
-
### OAuth Login
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
// Redirect to OAuth provider
|
|
52
|
+
```ts
|
|
136
53
|
const loginUrl = sso.auth.getLoginUrl('github', {
|
|
137
54
|
org: 'acme-corp',
|
|
138
55
|
service: 'main-app',
|
|
139
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
140
|
-
});
|
|
141
|
-
window.location.href = loginUrl;
|
|
142
|
-
|
|
143
|
-
// Handle callback - tokens are returned in URL fragment (#) for security
|
|
144
|
-
// (prevents tokens from being logged in server access logs)
|
|
145
|
-
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
|
146
|
-
const accessToken = hashParams.get('access_token');
|
|
147
|
-
|
|
148
|
-
if (accessToken) {
|
|
149
|
-
// Clear hash from URL for security
|
|
150
|
-
window.history.replaceState(null, '', window.location.pathname);
|
|
151
|
-
|
|
152
|
-
// Initialize SDK with OAuth token - automatically stored
|
|
153
|
-
const sso = new SsoClient({
|
|
154
|
-
baseURL: 'https://sso.example.com',
|
|
155
|
-
token: accessToken
|
|
156
|
-
});
|
|
157
|
-
// Token is now automatically stored and managed
|
|
158
|
-
window.location.href = '/dashboard';
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Password Authentication
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
// Register new user (tenant-first: always include org + service for proper attribution)
|
|
166
|
-
await sso.auth.register({
|
|
167
|
-
email: 'user@example.com',
|
|
168
|
-
password: 'SecurePass123!',
|
|
169
|
-
org_slug: 'acme-corp', // Your organization
|
|
170
|
-
service_slug: 'main-app' // Your application
|
|
171
|
-
});
|
|
172
|
-
// User identity is now scoped to your org and service
|
|
173
|
-
|
|
174
|
-
// Login with password (service-scoped: include org + service)
|
|
175
|
-
await sso.auth.login({
|
|
176
|
-
email: 'user@example.com',
|
|
177
|
-
password: 'SecurePass123!',
|
|
178
|
-
org_slug: 'acme-corp', // Your organization
|
|
179
|
-
service_slug: 'main-app' // Your application
|
|
56
|
+
redirect_uri: 'https://app.acme.com/callback',
|
|
180
57
|
});
|
|
181
|
-
// JWT is now scoped to org + service
|
|
182
|
-
|
|
183
|
-
// Enable MFA
|
|
184
|
-
const mfaSetup = await sso.user.mfa.setup();
|
|
185
|
-
console.log(mfaSetup.qr_code_svg); // Display QR code to user
|
|
186
|
-
|
|
187
|
-
// Verify and enable - automatically saves backup codes
|
|
188
|
-
const result = await sso.user.mfa.verify('123456'); // TOTP code from authenticator app
|
|
189
|
-
console.log('Backup codes:', result.backup_codes); // Save these securely!
|
|
190
58
|
```
|
|
191
59
|
|
|
192
|
-
### Hosted
|
|
60
|
+
### Hosted auth context
|
|
193
61
|
|
|
194
|
-
```
|
|
62
|
+
```ts
|
|
195
63
|
const context = await sso.auth.getContext({
|
|
196
64
|
org: 'acme-corp',
|
|
197
65
|
service: 'main-app',
|
|
198
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
console.log(context.organization?.name);
|
|
202
|
-
console.log(context.available_providers); // ['github', 'google', 'microsoft']
|
|
203
|
-
|
|
204
|
-
await sso.magicLinks.request({
|
|
205
|
-
email: 'user@example.com',
|
|
206
|
-
org_slug: 'acme-corp',
|
|
207
|
-
service_slug: 'main-app',
|
|
208
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
const passkeyLogin = await sso.passkeys.login('user@example.com', {
|
|
212
|
-
org_slug: 'acme-corp',
|
|
213
|
-
service_slug: 'main-app',
|
|
214
|
-
redirect_uri: 'https://app.acme.com/callback'
|
|
66
|
+
redirect_uri: 'https://app.acme.com/callback',
|
|
215
67
|
});
|
|
216
|
-
|
|
217
|
-
await sso.setSession({
|
|
218
|
-
access_token: passkeyLogin.access_token,
|
|
219
|
-
refresh_token: passkeyLogin.refresh_token
|
|
220
|
-
});
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
### Passkey Self-Service
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
const passkeys = await sso.passkeys.list();
|
|
227
|
-
|
|
228
|
-
if (passkeys.length > 0) {
|
|
229
|
-
await sso.passkeys.updateName(passkeys[0].id, 'Work Laptop');
|
|
230
|
-
await sso.passkeys.delete(passkeys[0].id);
|
|
231
|
-
}
|
|
232
68
|
```
|
|
233
69
|
|
|
234
|
-
###
|
|
70
|
+
### Provider token handoff
|
|
235
71
|
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
service: 'cli-tool'
|
|
72
|
+
```ts
|
|
73
|
+
const result = await sso.serviceApi.requestProviderToken({
|
|
74
|
+
user_id: 'user-id',
|
|
75
|
+
provider: 'github',
|
|
76
|
+
scopes: ['repo'],
|
|
242
77
|
});
|
|
243
|
-
|
|
244
|
-
console.log(`Visit: ${deviceAuth.verification_uri}`);
|
|
245
|
-
console.log(`Enter code: ${deviceAuth.user_code}`);
|
|
246
|
-
|
|
247
|
-
// Poll for token
|
|
248
|
-
const interval = setInterval(async () => {
|
|
249
|
-
try {
|
|
250
|
-
const tokens = await sso.auth.deviceCode.exchangeToken({
|
|
251
|
-
device_code: deviceAuth.device_code,
|
|
252
|
-
client_id: 'cli-client',
|
|
253
|
-
grant_type: 'urn:ietf:params:oauth:grant-type:device_code'
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Initialize SDK with the token - automatically stored
|
|
257
|
-
const authenticatedSso = new SsoClient({
|
|
258
|
-
baseURL: 'https://sso.example.com',
|
|
259
|
-
token: tokens.access_token
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
clearInterval(interval);
|
|
263
|
-
console.log('Authentication successful!');
|
|
264
|
-
} catch (error) {
|
|
265
|
-
// Continue polling...
|
|
266
|
-
}
|
|
267
|
-
}, deviceAuth.interval * 1000);
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Token Refresh
|
|
271
|
-
|
|
272
|
-
**Automatic Token Refresh** - The SDK automatically refreshes expired tokens when it detects a 401 error:
|
|
273
|
-
|
|
274
|
-
```typescript
|
|
275
|
-
// No manual refresh needed! Just make your API calls
|
|
276
|
-
try {
|
|
277
|
-
// If the access token is expired, the SDK will:
|
|
278
|
-
// 1. Detect the 401 error
|
|
279
|
-
// 2. Use the refresh token to get new tokens
|
|
280
|
-
// 3. Retry the request automatically
|
|
281
|
-
// 4. Return the result - you never see the 401!
|
|
282
|
-
const profile = await sso.user.getProfile();
|
|
283
|
-
console.log(profile);
|
|
284
|
-
} catch (error) {
|
|
285
|
-
// You'll only see errors if refresh fails (e.g., refresh token expired)
|
|
286
|
-
console.error('Session expired - please log in again');
|
|
287
|
-
// Redirect to login
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Optional: Manually refresh if needed (advanced use case)
|
|
291
|
-
const currentRefreshToken = await sso.getToken();
|
|
292
|
-
if (currentRefreshToken) {
|
|
293
|
-
const tokens = await sso.auth.refreshToken(currentRefreshToken);
|
|
294
|
-
// Tokens automatically updated
|
|
295
|
-
}
|
|
296
78
|
```
|
|
297
79
|
|
|
298
|
-
##
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
// Create organization
|
|
302
|
-
const org = await sso.organizations.create({
|
|
303
|
-
name: 'Acme Corp',
|
|
304
|
-
slug: 'acme-corp'
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
// Configure custom OAuth (BYOO - Bring Your Own OAuth)
|
|
308
|
-
await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
|
|
309
|
-
client_id: 'your-github-client-id',
|
|
310
|
-
client_secret: 'your-github-client-secret'
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// Invite team members
|
|
314
|
-
await sso.invitations.create('acme-corp', {
|
|
315
|
-
email: 'member@acme.com',
|
|
316
|
-
role: 'admin'
|
|
317
|
-
});
|
|
318
|
-
|
|
319
|
-
// Configure SMTP for transactional emails
|
|
320
|
-
await sso.organizations.setSmtp('acme-corp', {
|
|
321
|
-
host: 'smtp.gmail.com',
|
|
322
|
-
port: 587,
|
|
323
|
-
username: 'noreply@acme.com',
|
|
324
|
-
password: 'app-password',
|
|
325
|
-
from_email: 'noreply@acme.com',
|
|
326
|
-
from_name: 'Acme Corp'
|
|
327
|
-
});
|
|
328
|
-
```
|
|
329
|
-
|
|
330
|
-
## Subscription & Billing
|
|
331
|
-
|
|
332
|
-
The SDK provides provider-agnostic billing integration that works with both Stripe and Polar.
|
|
333
|
-
|
|
334
|
-
```typescript
|
|
335
|
-
// Check billing status
|
|
336
|
-
const billingInfo = await sso.organizations.billing.getInfo('acme-corp');
|
|
337
|
-
console.log(billingInfo.has_billing_account); // true/false
|
|
338
|
-
console.log(billingInfo.provider); // "stripe" or "polar"
|
|
339
|
-
|
|
340
|
-
// Open billing portal for subscription management
|
|
341
|
-
const portal = await sso.organizations.billing.createPortalSession('acme-corp', {
|
|
342
|
-
return_url: 'https://app.acme.com/settings/billing'
|
|
343
|
-
});
|
|
344
|
-
// Redirect user to manage their subscription
|
|
345
|
-
window.location.href = portal.url;
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
### BYOP - Bring Your Own Payment
|
|
349
|
-
|
|
350
|
-
Organizations can configure their own billing provider credentials to charge their end-users:
|
|
351
|
-
|
|
352
|
-
```typescript
|
|
353
|
-
// Configure organization's own Stripe credentials
|
|
354
|
-
await sso.organizations.billingCredentials.set('acme-corp', 'stripe', {
|
|
355
|
-
api_key: 'sk_live_...',
|
|
356
|
-
webhook_secret: 'whsec_...',
|
|
357
|
-
mode: 'live' // or 'test'
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// Check credential status
|
|
361
|
-
const status = await sso.organizations.billingCredentials.get('acme-corp', 'stripe');
|
|
362
|
-
console.log(status.configured); // true
|
|
363
|
-
console.log(status.mode); // "live"
|
|
364
|
-
|
|
365
|
-
// Remove credentials
|
|
366
|
-
await sso.organizations.billingCredentials.delete('acme-corp', 'stripe');
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
## Services & API Keys
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
// Create a service
|
|
373
|
-
const service = await sso.services.create('acme-corp', {
|
|
374
|
-
name: 'Main Application',
|
|
375
|
-
slug: 'main-app',
|
|
376
|
-
redirect_uris: ['https://app.acme.com/callback']
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
// Create API key for service-to-service auth
|
|
380
|
-
const apiKey = await sso.services.apiKeys.create('acme-corp', 'main-app', {
|
|
381
|
-
name: 'Production Backend',
|
|
382
|
-
expires_at: '2026-01-01T00:00:00Z'
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
console.log('API Key (save this):', apiKey.key);
|
|
386
|
-
|
|
387
|
-
// Use API key for backend authentication
|
|
388
|
-
const backendClient = new SsoClient({
|
|
389
|
-
baseURL: 'https://sso.example.com',
|
|
390
|
-
apiKey: apiKey.key
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
const rotated = await sso.services.rotateSecret('acme-corp', 'main-app');
|
|
394
|
-
console.log('New client secret:', rotated.client_secret);
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
## Fine-Grained Member Access
|
|
398
|
-
|
|
399
|
-
```typescript
|
|
400
|
-
const access = await sso.organizations.members.updateServiceAccess(
|
|
401
|
-
'acme-corp',
|
|
402
|
-
'user-id',
|
|
403
|
-
{
|
|
404
|
-
grants: [
|
|
405
|
-
{ service_slug: 'main-app', access: 'manager' },
|
|
406
|
-
{ service_slug: 'docs-app', access: 'viewer' }
|
|
407
|
-
]
|
|
408
|
-
}
|
|
409
|
-
);
|
|
410
|
-
|
|
411
|
-
console.log(access);
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
## Analytics
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
// Get login trends
|
|
418
|
-
const trends = await sso.analytics.getLoginTrends('acme-corp', {
|
|
419
|
-
start_date: '2025-01-01',
|
|
420
|
-
end_date: '2025-01-31'
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Get provider distribution
|
|
424
|
-
const byProvider = await sso.analytics.getLoginsByProvider('acme-corp');
|
|
425
|
-
console.log(byProvider); // [{ provider: 'github', count: 1523 }, ...]
|
|
426
|
-
|
|
427
|
-
// Get recent activity
|
|
428
|
-
const recentLogins = await sso.analytics.getRecentLogins('acme-corp', {
|
|
429
|
-
limit: 20
|
|
430
|
-
});
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
## Error Handling
|
|
434
|
-
|
|
435
|
-
```typescript
|
|
436
|
-
import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
|
|
437
|
-
|
|
438
|
-
try {
|
|
439
|
-
await sso.user.getProfile();
|
|
440
|
-
} catch (error) {
|
|
441
|
-
if (error instanceof SsoApiError) {
|
|
442
|
-
console.error(`API Error: ${error.message}`);
|
|
443
|
-
console.error(`Status: ${error.status}`);
|
|
444
|
-
console.error(`Code: ${error.errorCode}`);
|
|
445
|
-
|
|
446
|
-
if (error.status === 401) {
|
|
447
|
-
// Session expired (refresh token also expired)
|
|
448
|
-
// Automatic refresh already tried and failed
|
|
449
|
-
// Redirect to login
|
|
450
|
-
window.location.href = '/login';
|
|
451
|
-
} else if (error.status === 403) {
|
|
452
|
-
// Forbidden - insufficient permissions
|
|
453
|
-
console.error('You do not have permission to access this resource');
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
|
-
console.error('Unexpected error:', error);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
// React to authentication state changes
|
|
461
|
-
sso.onAuthStateChange((isAuthenticated) => {
|
|
462
|
-
if (!isAuthenticated) {
|
|
463
|
-
// User logged out or session expired
|
|
464
|
-
window.location.href = '/login';
|
|
465
|
-
}
|
|
466
|
-
});
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
## Platform Administration
|
|
470
|
-
|
|
471
|
-
For platform owners managing [AuthOS](https://authos.dev):
|
|
472
|
-
|
|
473
|
-
```typescript
|
|
474
|
-
// Approve pending organization
|
|
475
|
-
await sso.platform.organizations.approve('org-id', {
|
|
476
|
-
tier: 'professional',
|
|
477
|
-
reason: 'Verified enterprise customer'
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
// Promote user to platform owner
|
|
481
|
-
await sso.platform.promoteOwner({
|
|
482
|
-
email: 'admin@example.com'
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// Get platform analytics
|
|
486
|
-
const overview = await sso.platform.analytics.getOverview();
|
|
487
|
-
console.log(overview); // { total_users, total_orgs, total_logins, ... }
|
|
488
|
-
|
|
489
|
-
// Search users across all organizations
|
|
490
|
-
const users = await sso.platform.users.search('user@example.com');
|
|
491
|
-
|
|
492
|
-
// Inspect operational health
|
|
493
|
-
const ops = await sso.platform.getOperationsStatus();
|
|
494
|
-
console.log(ops.jobs_pending, ops.webhook_deliveries_failed);
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
## TypeScript Support
|
|
498
|
-
|
|
499
|
-
The SDK is written in TypeScript and includes complete type definitions:
|
|
500
|
-
|
|
501
|
-
```typescript
|
|
502
|
-
import type {
|
|
503
|
-
User,
|
|
504
|
-
Organization,
|
|
505
|
-
Service,
|
|
506
|
-
LoginTrendPoint,
|
|
507
|
-
RefreshTokenResponse,
|
|
508
|
-
CreateServicePayload,
|
|
509
|
-
UpdateServicePayload,
|
|
510
|
-
LoginPayload,
|
|
511
|
-
RegisterPayload,
|
|
512
|
-
SsoApiError
|
|
513
|
-
} from '@drmhse/sso-sdk';
|
|
514
|
-
|
|
515
|
-
// Example using types
|
|
516
|
-
const createService = async (payload: CreateServicePayload): Promise<Service> => {
|
|
517
|
-
return await sso.services.create('org-slug', payload);
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
const login = async (credentials: LoginPayload): Promise<RefreshTokenResponse> => {
|
|
521
|
-
return await sso.auth.login(credentials);
|
|
522
|
-
};
|
|
523
|
-
```
|
|
524
|
-
|
|
525
|
-
## Validating JWTs in Your Backend
|
|
526
|
-
|
|
527
|
-
[AuthOS](https://authos.dev) uses RS256 (asymmetric) JWT signing. Your backend can validate tokens without sharing secrets:
|
|
528
|
-
|
|
529
|
-
```typescript
|
|
530
|
-
// Fetch JWKS from the SSO platform
|
|
531
|
-
const jwksUrl = 'https://sso.example.com/.well-known/jwks.json';
|
|
532
|
-
const response = await fetch(jwksUrl);
|
|
533
|
-
const jwks = await response.json();
|
|
534
|
-
|
|
535
|
-
// Use a JWT library to verify tokens
|
|
536
|
-
import jwt from 'jsonwebtoken';
|
|
537
|
-
import jwksClient from 'jwks-rsa';
|
|
538
|
-
|
|
539
|
-
const client = jwksClient({ jwksUri: jwksUrl });
|
|
540
|
-
const key = await client.getSigningKey(header.kid);
|
|
541
|
-
const publicKey = key.getPublicKey();
|
|
542
|
-
|
|
543
|
-
const decoded = jwt.verify(token, publicKey, {
|
|
544
|
-
algorithms: ['RS256']
|
|
545
|
-
});
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
**[See Backend Validation Guide →](https://drmhse.com/docs/sso/api/concepts/token-validation)**
|
|
549
|
-
|
|
550
|
-
## Documentation
|
|
551
|
-
|
|
552
|
-
**[Complete documentation is available at drmhse.com/docs/sso](https://drmhse.com/docs/sso/)**
|
|
553
|
-
|
|
554
|
-
### Key Documentation Pages
|
|
555
|
-
|
|
556
|
-
- **[Getting Started](https://drmhse.com/docs/sso/sdk/getting-started)** - Installation and setup
|
|
557
|
-
- **[Authentication Flows](https://drmhse.com/docs/sso/sdk/guides/authentication-flows)** - OAuth, Device Flow, Admin Login
|
|
558
|
-
- **[Password Authentication](https://drmhse.com/docs/sso/sdk/guides/password-authentication)** - Register, Login, Reset Password
|
|
559
|
-
- **[MFA Management](https://drmhse.com/docs/sso/sdk/guides/mfa-management)** - TOTP setup and verification
|
|
560
|
-
- **[SDK Reference](https://drmhse.com/docs/sso/sdk/reference)** - Complete API reference
|
|
561
|
-
- **[API Reference](https://drmhse.com/docs/sso/api/reference)** - Backend API documentation
|
|
562
|
-
|
|
563
|
-
## Requirements
|
|
564
|
-
|
|
565
|
-
- **Node.js:** 18+ (for native fetch support)
|
|
566
|
-
- **Browsers:** All modern browsers with fetch support
|
|
567
|
-
- **TypeScript:** 4.5+ (optional, but recommended)
|
|
568
|
-
|
|
569
|
-
## License
|
|
80
|
+
## Feature highlights
|
|
570
81
|
|
|
571
|
-
|
|
82
|
+
- Password, OAuth, magic-link, passkey, MFA, and device-flow authentication
|
|
83
|
+
- Hosted auth context for login surfaces
|
|
84
|
+
- Linked accounts and provider-token request completion flows
|
|
85
|
+
- Organization, service, analytics, audit-log, and platform-owner APIs
|
|
86
|
+
- Service API helpers including backend-only provider token retrieval
|
|
572
87
|
|
|
573
|
-
##
|
|
88
|
+
## Canonical references
|
|
574
89
|
|
|
575
|
-
-
|
|
576
|
-
-
|
|
577
|
-
-
|
|
90
|
+
- SDK getting started: [authos.dev/docs/sdk/getting-started/](https://authos.dev/docs/sdk/getting-started/)
|
|
91
|
+
- SDK reference: [authos.dev/docs/sdk/reference/](https://authos.dev/docs/sdk/reference/)
|
|
92
|
+
- API reference: [authos.dev/docs/api/reference/](https://authos.dev/docs/api/reference/)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drmhse/sso-sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Zero-dependency TypeScript SDK for AuthOS, the multi-tenant authentication platform",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -44,18 +44,17 @@
|
|
|
44
44
|
],
|
|
45
45
|
"author": "DRM HSE <info@drmhse.com>",
|
|
46
46
|
"license": "MIT",
|
|
47
|
-
"dependencies": {},
|
|
48
47
|
"devDependencies": {
|
|
49
48
|
"@types/node": "^20.11.5",
|
|
50
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
51
|
-
"@typescript-eslint/parser": "^
|
|
49
|
+
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
|
50
|
+
"@typescript-eslint/parser": "^8.59.4",
|
|
52
51
|
"eslint": "^8.56.0",
|
|
53
52
|
"tsup": "^8.0.1",
|
|
54
53
|
"typescript": "^5.3.3"
|
|
55
54
|
},
|
|
56
55
|
"repository": {
|
|
57
56
|
"type": "git",
|
|
58
|
-
"url": "https://github.com/drmhse/
|
|
57
|
+
"url": "https://github.com/drmhse/AuthOS.git",
|
|
59
58
|
"directory": "sso-sdk"
|
|
60
59
|
},
|
|
61
60
|
"publishConfig": {
|