@drmhse/sso-sdk 0.2.3 → 0.2.6

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 CHANGED
@@ -1,15 +1,24 @@
1
1
  # SSO Platform SDK
2
2
 
3
- A zero-dependency, strongly-typed TypeScript SDK for interacting with the multi-tenant SSO Platform API.
3
+ [![npm version](https://img.shields.io/npm/v/@drmhse/sso-sdk)](https://www.npmjs.com/package/@drmhse/sso-sdk)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ A zero-dependency, strongly-typed TypeScript SDK for the multi-tenant SSO Platform API.
7
+
8
+ **📚 [View Full Documentation →](https://drmhse.com/docs/sso/)**
4
9
 
5
10
  ## Features
6
11
 
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.
12
+ -**Zero Dependencies** - Built on native `fetch` API
13
+ - **Strongly Typed** - Complete TypeScript definitions
14
+ -**Framework Agnostic** - Works in any JavaScript environment
15
+ - **OAuth 2.0 Flows** - Support for GitHub, Google, Microsoft
16
+ -**Password Authentication** - Native email/password auth with MFA
17
+ -**Device Flow** - RFC 8628 for CLIs and headless apps
18
+ - ✅ **Multi-Factor Authentication** - TOTP-based 2FA with backup codes
19
+ - ✅ **Organization Management** - Multi-tenant with RBAC
20
+ - ✅ **Analytics & Audit Logs** - Track authentication and administrative actions
21
+ - ✅ **SAML 2.0** - Act as Identity Provider
13
22
 
14
23
  ## Installation
15
24
 
@@ -20,38 +29,37 @@ npm install @drmhse/sso-sdk
20
29
  ## Quick Start
21
30
 
22
31
  ```typescript
23
- import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
32
+ import { SsoClient } from '@drmhse/sso-sdk';
24
33
 
25
34
  // Initialize the client
26
35
  const sso = new SsoClient({
27
36
  baseURL: 'https://sso.example.com',
28
- token: localStorage.getItem('sso_token') // Optional initial token
37
+ token: localStorage.getItem('sso_access_token')
29
38
  });
30
39
 
31
- // Use the SDK
32
- async function example() {
33
- try {
34
- // Get user profile
35
- const profile = await sso.user.getProfile();
36
- console.log(profile.email);
40
+ // Get user profile
41
+ const profile = await sso.user.getProfile();
42
+ console.log(profile.email);
37
43
 
38
- // List organizations
39
- const orgs = await sso.organizations.list();
40
- console.log(orgs);
41
- } catch (error) {
42
- if (error instanceof SsoApiError) {
43
- console.error(`API Error: ${error.message} (${error.errorCode})`);
44
- }
45
- }
46
- }
44
+ // List organizations
45
+ const orgs = await sso.organizations.list();
46
+ console.log(orgs);
47
47
  ```
48
48
 
49
- ## Authentication Flows
49
+ **New to the SDK?** Start with the **[Getting Started Guide →](https://drmhse.com/docs/sso/sdk/getting-started)** for a step-by-step tutorial showing how to authenticate users from scratch, handle token refresh, and implement logout.
50
50
 
51
- ### End-User OAuth Login
51
+ **Essential Guides:**
52
+ - **[Authentication Flows](https://drmhse.com/docs/sso/sdk/guides/authentication-flows)** - OAuth, Device Flow, and Admin Login patterns
53
+ - **[Password Authentication](https://drmhse.com/docs/sso/sdk/guides/password-authentication)** - Registration, login, and password reset
54
+ - **[MFA Management](https://drmhse.com/docs/sso/sdk/guides/mfa-management)** - TOTP-based 2FA implementation
55
+ - **[Error Handling](https://drmhse.com/docs/sso/sdk/guides/error-handling)** - Best practices for handling API errors
56
+
57
+ ## Authentication Examples
58
+
59
+ ### OAuth Login
52
60
 
53
61
  ```typescript
54
- // 1. Redirect user to OAuth provider
62
+ // Redirect to OAuth provider
55
63
  const loginUrl = sso.auth.getLoginUrl('github', {
56
64
  org: 'acme-corp',
57
65
  service: 'main-app',
@@ -59,382 +67,290 @@ const loginUrl = sso.auth.getLoginUrl('github', {
59
67
  });
60
68
  window.location.href = loginUrl;
61
69
 
62
- // 2. In your callback handler, extract both tokens from the URL
70
+ // Handle callback
63
71
  const params = new URLSearchParams(window.location.search);
64
72
  const accessToken = params.get('access_token');
65
73
  const refreshToken = params.get('refresh_token');
66
74
 
67
75
  if (accessToken && refreshToken) {
68
76
  sso.setAuthToken(accessToken);
69
- localStorage.setItem('sso_token', accessToken);
77
+ localStorage.setItem('sso_access_token', accessToken);
70
78
  localStorage.setItem('sso_refresh_token', refreshToken);
71
79
  }
72
80
  ```
73
81
 
74
- ### Admin Login
82
+ ### Password Authentication
75
83
 
76
84
  ```typescript
77
- const adminUrl = sso.auth.getAdminLoginUrl('github', {
78
- org_slug: 'acme-corp' // Optional: directs user to a specific org dashboard after login
85
+ // Register new user
86
+ await sso.auth.register({
87
+ email: 'user@example.com',
88
+ password: 'SecurePass123!',
89
+ org_slug: 'acme-corp'
79
90
  });
80
- window.location.href = adminUrl;
91
+
92
+ // Login with password
93
+ const tokens = await sso.auth.login({
94
+ email: 'user@example.com',
95
+ password: 'SecurePass123!'
96
+ });
97
+ sso.setAuthToken(tokens.access_token);
98
+
99
+ // Enable MFA
100
+ const mfaSetup = await sso.user.mfa.setup();
101
+ console.log(mfaSetup.qr_code); // Display QR code to user
102
+
103
+ // Verify and enable
104
+ await sso.user.mfa.verify('123456'); // TOTP code from authenticator app
81
105
  ```
82
106
 
83
107
  ### Device Flow (for CLIs)
84
108
 
85
- This flow involves both the CLI and a web browser.
86
-
87
109
  ```typescript
88
- // --- In your CLI Application ---
89
-
90
- // 1. Request device code
110
+ // In your CLI application
91
111
  const deviceAuth = await sso.auth.deviceCode.request({
92
- client_id: 'cli-client-id',
112
+ client_id: 'cli-client',
93
113
  org: 'acme-corp',
94
- service: 'acme-cli'
114
+ service: 'cli-tool'
95
115
  });
96
116
 
97
117
  console.log(`Visit: ${deviceAuth.verification_uri}`);
98
118
  console.log(`Enter code: ${deviceAuth.user_code}`);
99
119
 
100
- // 4. 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);
120
+ // Poll for token
121
+ const interval = setInterval(async () => {
122
+ try {
123
+ const tokens = await sso.auth.deviceCode.exchangeToken({
124
+ device_code: deviceAuth.device_code,
125
+ client_id: 'cli-client'
126
+ });
111
127
 
112
- // 3. Redirect user to the appropriate login flow, passing the user_code
113
- // This links the browser session to the device waiting for authorization.
114
- const loginUrl = sso.auth.getLoginUrl('github', {
115
- org: context.org_slug,
116
- service: context.service_slug,
117
- user_code: userEnteredCode, // CRITICAL: Pass user_code here
118
- });
119
- window.location.href = loginUrl; // User logs in, authorizing the device
128
+ sso.setAuthToken(tokens.access_token);
129
+ clearInterval(interval);
130
+ console.log(' Authentication successful!');
131
+ } catch (error) {
132
+ // Continue polling...
133
+ }
134
+ }, deviceAuth.interval * 1000);
120
135
  ```
121
136
 
122
- ### Refreshing Tokens
123
-
124
- Renew an expired access token using a refresh token. This uses token rotation for enhanced security.
137
+ ### Token Refresh
125
138
 
126
139
  ```typescript
127
140
  try {
128
141
  const tokens = await sso.auth.refreshToken(storedRefreshToken);
129
-
130
- // Update tokens in your application state and storage
142
+
131
143
  sso.setAuthToken(tokens.access_token);
132
- localStorage.setItem('sso_token', tokens.access_token);
144
+ localStorage.setItem('sso_access_token', tokens.access_token);
133
145
  localStorage.setItem('sso_refresh_token', tokens.refresh_token);
134
146
  } catch (error) {
135
- // Refresh failed, user needs to log in again
136
- console.error("Token refresh failed:", error);
147
+ // Refresh failed - redirect to login
148
+ console.error('Token refresh failed:', error);
137
149
  }
138
150
  ```
139
151
 
140
- ### Logout
141
-
142
- ```typescript
143
- await sso.auth.logout();
144
- sso.setAuthToken(null);
145
- localStorage.removeItem('sso_token');
146
- localStorage.removeItem('sso_refresh_token');
147
- ```
148
-
149
- ## Validating Tokens in Your Backend
150
-
151
- The SSO platform uses **RS256** (RSA with SHA-256) asymmetric signing for JWTs. This means your backend services can validate JWT signatures without needing access to any shared secrets.
152
-
153
- ### How It Works
154
-
155
- 1. **Fetch the JWKS**: The SSO platform exposes a public JWKS (JSON Web Key Set) endpoint at `/.well-known/jwks.json` containing the public RSA key(s).
156
- 2. **Cache the Keys**: Fetch and cache the JWKS in your backend to avoid repeated requests.
157
- 3. **Verify Tokens**: When a client sends a JWT, extract the `kid` (Key ID) from the token header, find the matching key in your cached JWKS, and verify the signature.
158
- 4. **Validate Claims**: After signature verification, validate token claims like `exp` (expiration), `iss` (issuer), and `aud` (audience).
159
-
160
- ### Node.js/Express Example
161
-
162
- Here's a complete example of JWT validation middleware:
152
+ ## Organization Management
163
153
 
164
154
  ```typescript
165
- import { expressjwt } from 'express-jwt';
166
- import jwksRsa from 'jwks-rsa';
167
-
168
- // Configure JWKS client to fetch public keys
169
- const jwksClient = jwksRsa({
170
- cache: true,
171
- rateLimit: true,
172
- jwksRequestsPerMinute: 5,
173
- jwksUri: 'https://sso.example.com/.well-known/jwks.json'
155
+ // Create organization
156
+ const org = await sso.organizations.createPublic({
157
+ name: 'Acme Corp',
158
+ slug: 'acme-corp'
174
159
  });
175
160
 
176
- // Function to get signing key from JWKS
177
- function getKey(header, callback) {
178
- jwksClient.getSigningKey(header.kid, (err, key) => {
179
- if (err) {
180
- return callback(err);
181
- }
182
- const signingKey = key.getPublicKey();
183
- callback(null, signingKey);
184
- });
185
- }
161
+ // Configure custom OAuth (BYOO - Bring Your Own OAuth)
162
+ await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
163
+ client_id: 'your-github-client-id',
164
+ client_secret: 'your-github-client-secret'
165
+ });
186
166
 
187
- // JWT validation middleware
188
- const requireAuth = expressjwt({
189
- secret: getKey,
190
- algorithms: ['RS256'],
191
- credentialsRequired: true,
192
- getToken: (req) => {
193
- if (req.headers.authorization?.startsWith('Bearer ')) {
194
- return req.headers.authorization.substring(7);
195
- }
196
- return null;
197
- }
167
+ // Invite team members
168
+ await sso.organizations.invitations.create('acme-corp', {
169
+ email: 'member@acme.com',
170
+ role: 'admin'
198
171
  });
199
172
 
200
- // Use in your routes
201
- app.get('/api/protected', requireAuth, (req, res) => {
202
- // req.auth contains the decoded JWT claims
203
- const { sub, email, org, service } = req.auth;
204
- res.json({ message: `Hello ${email}` });
173
+ // Configure SMTP for transactional emails
174
+ await sso.organizations.setSmtp('acme-corp', {
175
+ host: 'smtp.gmail.com',
176
+ port: 587,
177
+ username: 'noreply@acme.com',
178
+ password: 'app-password',
179
+ from_email: 'noreply@acme.com',
180
+ from_name: 'Acme Corp'
205
181
  });
206
182
  ```
207
183
 
208
- ### Manual Validation (Node.js)
209
-
210
- If you prefer to validate manually without middleware:
184
+ ## Services & API Keys
211
185
 
212
186
  ```typescript
213
- import jwt from 'jsonwebtoken';
214
- import jwksRsa from 'jwks-rsa';
215
-
216
- const jwksClient = jwksRsa({
217
- jwksUri: 'https://sso.example.com/.well-known/jwks.json'
187
+ // Create a service
188
+ const service = await sso.services.create('acme-corp', {
189
+ name: 'Main Application',
190
+ slug: 'main-app',
191
+ redirect_uris: ['https://app.acme.com/callback']
218
192
  });
219
193
 
220
- async function validateToken(token: string) {
221
- try {
222
- // Decode without verifying to get the kid
223
- const decoded = jwt.decode(token, { complete: true });
224
- if (!decoded || !decoded.header.kid) {
225
- throw new Error('Invalid token: missing kid');
226
- }
227
-
228
- // Get the public key for this kid
229
- const key = await jwksClient.getSigningKey(decoded.header.kid);
230
- const publicKey = key.getPublicKey();
194
+ // Create API key for service-to-service auth
195
+ const apiKey = await sso.services.apiKeys.create('acme-corp', 'main-app', {
196
+ name: 'Production Backend',
197
+ expires_at: '2026-01-01T00:00:00Z'
198
+ });
231
199
 
232
- // Verify and decode the token
233
- const verified = jwt.verify(token, publicKey, {
234
- algorithms: ['RS256']
235
- });
200
+ console.log('API Key (save this):', apiKey.key);
236
201
 
237
- return verified; // Returns the decoded claims
238
- } catch (error) {
239
- console.error('Token validation failed:', error);
240
- throw error;
241
- }
242
- }
243
-
244
- // Usage
245
- const claims = await validateToken(req.headers.authorization.split(' ')[1]);
246
- console.log(claims.email, claims.org, claims.service);
202
+ // Use API key for backend authentication
203
+ const backendClient = new SsoClient({
204
+ baseURL: 'https://sso.example.com',
205
+ apiKey: apiKey.key
206
+ });
247
207
  ```
248
208
 
249
- ### Other Languages
250
-
251
- The same approach works in any language:
252
-
253
- - **Python**: Use `PyJWT` with `python-jose` or `jwcrypto`
254
- - **Go**: Use `golang-jwt/jwt` with JWKS support
255
- - **Java**: Use `java-jwt` or Spring Security with JWKS
256
- - **Ruby**: Use `jwt` gem with `jwks-ruby`
257
-
258
- The key steps are always the same:
259
- 1. Fetch JWKS from `/.well-known/jwks.json`
260
- 2. Extract `kid` from JWT header
261
- 3. Find matching key in JWKS
262
- 4. Verify signature using the public key
263
- 5. Validate token claims (especially `exp`)
264
-
265
- ## API Reference
266
-
267
- ### Analytics (`sso.analytics`)
268
-
269
- Provides login tracking and metrics for a specific organization.
209
+ ## Analytics
270
210
 
271
211
  ```typescript
272
- // Get login trends over time
212
+ // Get login trends
273
213
  const trends = await sso.analytics.getLoginTrends('acme-corp', {
274
214
  start_date: '2025-01-01',
275
215
  end_date: '2025-01-31'
276
216
  });
277
- ```
278
-
279
- ### Authentication (`sso.auth`)
280
217
 
281
- Handles all authentication flows, including OAuth, device flow, and token management.
218
+ // Get provider distribution
219
+ const byProvider = await sso.analytics.getLoginsByProvider('acme-corp');
220
+ console.log(byProvider); // [{ provider: 'github', count: 1523 }, ...]
282
221
 
283
- ```typescript
284
- // Get a fresh OAuth token for an external provider (e.g., GitHub)
285
- const githubToken = await sso.auth.getProviderToken('github');
286
- // Use githubToken.access_token to make GitHub API calls
287
- ```
288
-
289
- ### Organizations (`sso.organizations`)
290
-
291
- Manages organizations, members, and BYOO credentials.
292
-
293
- ```typescript
294
- // Create organization (public endpoint)
295
- const org = await sso.organizations.createPublic({
296
- slug: 'acme-corp',
297
- name: 'Acme Corporation',
298
- owner_email: 'founder@acme.com'
299
- });
300
-
301
- // BYOO: Set custom OAuth credentials
302
- await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
303
- client_id: 'Iv1.abc123',
304
- client_secret: 'secret-value'
222
+ // Get recent activity
223
+ const recentLogins = await sso.analytics.getRecentLogins('acme-corp', {
224
+ limit: 20
305
225
  });
306
226
  ```
307
227
 
308
- #### End-User Management (`sso.organizations.endUsers`)
309
-
310
- Manage your organization's customers (end-users with subscriptions).
228
+ ## Error Handling
311
229
 
312
230
  ```typescript
313
- // List all end-users for an organization
314
- const endUsers = await sso.organizations.endUsers.list('acme-corp', {
315
- page: 1,
316
- limit: 20
317
- });
231
+ import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
318
232
 
319
- // Revoke all active sessions for a specific end-user
320
- await sso.organizations.endUsers.revokeSessions('acme-corp', 'user-id-123');
233
+ try {
234
+ await sso.user.getProfile();
235
+ } catch (error) {
236
+ if (error instanceof SsoApiError) {
237
+ console.error(`API Error: ${error.message}`);
238
+ console.error(`Status: ${error.status}`);
239
+ console.error(`Code: ${error.errorCode}`);
240
+
241
+ if (error.status === 401) {
242
+ // Token expired - refresh or re-authenticate
243
+ } else if (error.status === 403) {
244
+ // Forbidden - insufficient permissions
245
+ }
246
+ } else {
247
+ console.error('Unexpected error:', error);
248
+ }
249
+ }
321
250
  ```
322
251
 
323
- ### Services & Plans (`sso.services`)
252
+ ## Platform Administration
324
253
 
325
- Manages the applications (services) and subscription plans for an organization.
254
+ For platform owners managing the entire SSO system:
326
255
 
327
256
  ```typescript
328
- // Create a service
329
- const result = await sso.services.create('acme-corp', {
330
- slug: 'main-app',
331
- name: 'Main Application',
332
- service_type: 'web',
333
- redirect_uris: ['https://app.acme.com/callback']
257
+ // Approve pending organization
258
+ await sso.platform.organizations.approve('org-id', {
259
+ tier: 'professional',
260
+ reason: 'Verified enterprise customer'
334
261
  });
335
262
 
336
- // Create a subscription plan for that service
337
- await sso.services.plans.create('acme-corp', 'main-app', {
338
- name: 'pro',
339
- price_cents: 2999, // Note: price is in cents
340
- currency: 'usd',
341
- features: ['api-access', 'priority-support']
263
+ // Promote user to platform owner
264
+ await sso.platform.promoteOwner({
265
+ email: 'admin@example.com'
342
266
  });
267
+
268
+ // Get platform analytics
269
+ const overview = await sso.platform.analytics.getOverview();
270
+ console.log(overview); // { total_users, total_orgs, total_logins, ... }
271
+
272
+ // Search users across all organizations
273
+ const users = await sso.platform.users.search('user@example.com');
343
274
  ```
344
275
 
345
- ### User Profile & Identities (`sso.user`)
276
+ ## TypeScript Support
346
277
 
347
- Manages the authenticated user's own profile and linked social accounts.
278
+ The SDK is written in TypeScript and includes complete type definitions:
348
279
 
349
280
  ```typescript
350
- // Get profile
351
- const profile = await sso.user.getProfile();
281
+ import type {
282
+ User,
283
+ Organization,
284
+ Service,
285
+ LoginTrendPoint,
286
+ RefreshTokenResponse,
287
+ CreateServicePayload,
288
+ UpdateServicePayload,
289
+ LoginPayload,
290
+ RegisterPayload,
291
+ SsoApiError
292
+ } from '@drmhse/sso-sdk';
352
293
 
353
- // Start linking a new social account
354
- const { authorization_url } = await sso.user.identities.startLink('google');
355
- window.location.href = authorization_url;
294
+ // Example using types
295
+ const createService = async (payload: CreateServicePayload): Promise<Service> => {
296
+ return await sso.services.create('org-slug', payload);
297
+ };
356
298
 
357
- // Unlink a social account
358
- await sso.user.identities.unlink('github');
299
+ const login = async (credentials: LoginPayload): Promise<RefreshTokenResponse> => {
300
+ return await sso.auth.login(credentials);
301
+ };
359
302
  ```
360
303
 
361
- ### Invitations (`sso.invitations`)
304
+ ## Validating JWTs in Your Backend
362
305
 
363
- Manages team invitations for an organization.
306
+ The SSO platform uses RS256 (asymmetric) JWT signing. Your backend can validate tokens without sharing secrets:
364
307
 
365
308
  ```typescript
366
- // Create and send an invitation
367
- await sso.invitations.create('acme-corp', {
368
- email: 'new-dev@example.com',
369
- role: 'member'
370
- });
309
+ // Fetch JWKS from the SSO platform
310
+ const jwksUrl = 'https://sso.example.com/.well-known/jwks.json';
311
+ const response = await fetch(jwksUrl);
312
+ const jwks = await response.json();
371
313
 
372
- // List invitations for the current user
373
- const myInvites = await sso.invitations.listForUser();
374
- ```
375
-
376
- ### Platform Administration (`sso.platform`)
377
-
378
- Platform owner methods require a Platform Owner JWT.
314
+ // Use a JWT library to verify tokens
315
+ import jwt from 'jsonwebtoken';
316
+ import jwksClient from 'jwks-rsa';
379
317
 
380
- ```typescript
381
- // List all organizations awaiting approval
382
- const pendingOrgs = await sso.platform.organizations.list({
383
- status: 'pending'
384
- });
318
+ const client = jwksClient({ jwksUri: jwksUrl });
319
+ const key = await client.getSigningKey(header.kid);
320
+ const publicKey = key.getPublicKey();
385
321
 
386
- // Approve an organization
387
- await sso.platform.organizations.approve('org-id-123', {
388
- tier_id: 'tier-starter'
322
+ const decoded = jwt.verify(token, publicKey, {
323
+ algorithms: ['RS256']
389
324
  });
390
325
  ```
391
326
 
392
- #### Platform Analytics (`sso.platform.analytics`)
327
+ **📚 [See Backend Validation Guide →](https://drmhse.com/docs/sso/api/concepts/token-validation)**
393
328
 
394
- Provides platform-wide metrics for platform owners.
329
+ ## Documentation
395
330
 
396
- ```typescript
397
- // Get platform-wide analytics overview
398
- const overview = await sso.platform.analytics.getOverview();
399
- console.log(`Total Users: ${overview.total_users}`);
400
- ```
331
+ **[Complete documentation is available at drmhse.com/docs/sso](https://drmhse.com/docs/sso/)**
401
332
 
402
- ## Error Handling
333
+ ### Key Documentation Pages
403
334
 
404
- The SDK throws `SsoApiError` for all API errors:
335
+ - **[Getting Started](https://drmhse.com/docs/sso/sdk/getting-started)** - Installation and setup
336
+ - **[Authentication Flows](https://drmhse.com/docs/sso/sdk/guides/authentication-flows)** - OAuth, Device Flow, Admin Login
337
+ - **[Password Authentication](https://drmhse.com/docs/sso/sdk/guides/password-authentication)** - Register, Login, Reset Password
338
+ - **[MFA Management](https://drmhse.com/docs/sso/sdk/guides/mfa-management)** - TOTP setup and verification
339
+ - **[SDK Reference](https://drmhse.com/docs/sso/sdk/reference)** - Complete API reference
340
+ - **[API Reference](https://drmhse.com/docs/sso/api/reference)** - Backend API documentation
405
341
 
406
- ```typescript
407
- import { SsoApiError } from '@drmhse/sso-sdk';
342
+ ## Requirements
408
343
 
409
- try {
410
- await sso.organizations.get('non-existent');
411
- } catch (error) {
412
- if (error instanceof SsoApiError) {
413
- console.error(`Error ${error.statusCode}: ${error.message}`);
414
- console.error(`Code: ${error.errorCode}`);
344
+ - **Node.js:** 18+ (for native fetch support)
345
+ - **Browsers:** All modern browsers with fetch support
346
+ - **TypeScript:** 4.5+ (optional, but recommended)
415
347
 
416
- if (error.isAuthError()) {
417
- // Redirect to login
418
- }
419
- }
420
- }
421
- ```
422
-
423
- ## TypeScript
348
+ ## License
424
349
 
425
- The SDK is written in TypeScript and includes complete type definitions.
350
+ MIT © [DRM HSE](https://github.com/drmhse)
426
351
 
427
- ```typescript
428
- import type {
429
- Organization,
430
- Service,
431
- User,
432
- JwtClaims,
433
- SsoApiError,
434
- // ... and many more types
435
- } from '@drmhse/sso-sdk';
436
- ```
437
-
438
- ## License
352
+ ## Support
439
353
 
440
- MIT
354
+ - **Documentation:** [drmhse.com/docs/sso](https://drmhse.com/docs/sso/)
355
+ - **Issues:** [GitHub Issues](https://github.com/drmhse/sso/issues)
356
+ - **Email:** [info@drmhse.com](mailto:info@drmhse.com)