@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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 DRM HSE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,400 @@
1
+ # SSO Platform SDK
2
+
3
+ A zero-dependency, strongly-typed TypeScript SDK for interacting with the multi-tenant SSO Platform API.
4
+
5
+ ## Features
6
+
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
+ - **Modular & Tree-Shakable**: Import only what you need
13
+ - **Comprehensive Documentation**: Full TSDoc comments for excellent IDE support
14
+ - **Modern**: Supports Node.js 18+ and all modern browsers
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @drmhse/sso-sdk
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { SsoClient } from '@drmhse/sso-sdk';
26
+
27
+ // Initialize the client
28
+ const sso = new SsoClient({
29
+ baseURL: 'https://sso.example.com',
30
+ token: localStorage.getItem('jwt') // Optional initial token
31
+ });
32
+
33
+ // Use the SDK
34
+ async function example() {
35
+ try {
36
+ // Get user profile
37
+ const profile = await sso.user.getProfile();
38
+ console.log(profile.email);
39
+
40
+ // List organizations
41
+ const orgs = await sso.organizations.list();
42
+ console.log(orgs);
43
+ } catch (error) {
44
+ if (error instanceof SsoApiError) {
45
+ console.error(`API Error: ${error.message} (${error.errorCode})`);
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Authentication Flows
52
+
53
+ ### End-User OAuth Login
54
+
55
+ ```typescript
56
+ // Redirect user to OAuth provider
57
+ const loginUrl = sso.auth.getLoginUrl('github', {
58
+ org: 'acme-corp',
59
+ service: 'main-app',
60
+ redirect_uri: 'https://app.acme.com/callback'
61
+ });
62
+ window.location.href = loginUrl;
63
+
64
+ // In your callback handler
65
+ const token = new URLSearchParams(window.location.search).get('token');
66
+ if (token) {
67
+ sso.setAuthToken(token);
68
+ localStorage.setItem('jwt', token);
69
+ }
70
+ ```
71
+
72
+ ### Admin Login
73
+
74
+ ```typescript
75
+ const adminUrl = sso.auth.getAdminLoginUrl('github', {
76
+ org_slug: 'acme-corp' // Optional
77
+ });
78
+ window.location.href = adminUrl;
79
+ ```
80
+
81
+ ### Device Flow (for CLIs)
82
+
83
+ ```typescript
84
+ // Step 1: Request device code
85
+ const deviceAuth = await sso.auth.deviceCode.request({
86
+ client_id: 'cli-client-id',
87
+ org: 'acme-corp',
88
+ service: 'acme-cli'
89
+ });
90
+
91
+ console.log(`Visit ${deviceAuth.verification_uri}`);
92
+ console.log(`Enter code: ${deviceAuth.user_code}`);
93
+
94
+ // Step 2: Poll for token
95
+ const pollInterval = setInterval(async () => {
96
+ try {
97
+ const token = await sso.auth.deviceCode.exchangeToken({
98
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
99
+ device_code: deviceAuth.device_code,
100
+ client_id: 'cli-client-id'
101
+ });
102
+
103
+ clearInterval(pollInterval);
104
+ sso.setAuthToken(token.access_token);
105
+ console.log('Authenticated!');
106
+ } catch (error) {
107
+ if (error.errorCode !== 'authorization_pending') {
108
+ clearInterval(pollInterval);
109
+ throw error;
110
+ }
111
+ }
112
+ }, deviceAuth.interval * 1000);
113
+ ```
114
+
115
+ ### Logout
116
+
117
+ ```typescript
118
+ await sso.auth.logout();
119
+ sso.setAuthToken(null);
120
+ localStorage.removeItem('jwt');
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### Organizations
126
+
127
+ ```typescript
128
+ // Create organization (public endpoint)
129
+ const org = await sso.organizations.createPublic({
130
+ slug: 'acme-corp',
131
+ name: 'Acme Corporation',
132
+ owner_email: 'founder@acme.com'
133
+ });
134
+
135
+ // List user's organizations
136
+ const orgs = await sso.organizations.list({ status: 'active' });
137
+
138
+ // Get organization details
139
+ const details = await sso.organizations.get('acme-corp');
140
+
141
+ // Update organization
142
+ await sso.organizations.update('acme-corp', {
143
+ name: 'Acme Corp Inc.'
144
+ });
145
+
146
+ // Manage members
147
+ const members = await sso.organizations.members.list('acme-corp');
148
+ await sso.organizations.members.updateRole('acme-corp', 'user-id', {
149
+ role: 'admin'
150
+ });
151
+ await sso.organizations.members.remove('acme-corp', 'user-id');
152
+
153
+ // BYOO: Set custom OAuth credentials
154
+ await sso.organizations.oauthCredentials.set('acme-corp', 'github', {
155
+ client_id: 'Iv1.abc123',
156
+ client_secret: 'secret-value'
157
+ });
158
+ ```
159
+
160
+ ### Services
161
+
162
+ ```typescript
163
+ // Create service
164
+ const service = await sso.services.create('acme-corp', {
165
+ slug: 'main-app',
166
+ name: 'Main Application',
167
+ service_type: 'web',
168
+ github_scopes: ['user:email', 'read:org'],
169
+ redirect_uris: ['https://app.acme.com/callback']
170
+ });
171
+
172
+ // List services
173
+ const services = await sso.services.list('acme-corp');
174
+
175
+ // Get service details
176
+ const details = await sso.services.get('acme-corp', 'main-app');
177
+
178
+ // Update service
179
+ await sso.services.update('acme-corp', 'main-app', {
180
+ redirect_uris: ['https://app.acme.com/callback', 'https://app.acme.com/oauth']
181
+ });
182
+
183
+ // Delete service
184
+ await sso.services.delete('acme-corp', 'old-service');
185
+
186
+ // Manage plans
187
+ const plan = await sso.services.plans.create('acme-corp', 'main-app', {
188
+ name: 'pro',
189
+ price_monthly: 29.99,
190
+ features: ['api-access', 'advanced-analytics']
191
+ });
192
+ ```
193
+
194
+ ### Invitations
195
+
196
+ ```typescript
197
+ // Send invitation
198
+ const invitation = await sso.invitations.create('acme-corp', {
199
+ invitee_email: 'newuser@example.com',
200
+ role: 'member'
201
+ });
202
+
203
+ // List organization's invitations
204
+ const orgInvites = await sso.invitations.listForOrg('acme-corp');
205
+
206
+ // List user's invitations
207
+ const myInvites = await sso.invitations.listForUser();
208
+
209
+ // Accept invitation
210
+ await sso.invitations.accept('invitation-token');
211
+
212
+ // Decline invitation
213
+ await sso.invitations.decline('invitation-token');
214
+
215
+ // Cancel invitation
216
+ await sso.invitations.cancel('acme-corp', 'invitation-id');
217
+ ```
218
+
219
+ ### User Profile
220
+
221
+ ```typescript
222
+ // Get profile
223
+ const profile = await sso.user.getProfile();
224
+
225
+ // Update profile
226
+ await sso.user.updateProfile({ email: 'newemail@example.com' });
227
+
228
+ // Get subscription
229
+ const subscription = await sso.user.getSubscription();
230
+ ```
231
+
232
+ ### Provider Tokens
233
+
234
+ ```typescript
235
+ // Get fresh OAuth token for external provider
236
+ const githubToken = await sso.auth.getProviderToken('github');
237
+ // Use githubToken.access_token to make GitHub API calls
238
+ ```
239
+
240
+ ### Platform Administration
241
+
242
+ Platform owner methods require a Platform Owner JWT.
243
+
244
+ ```typescript
245
+ // List all organizations
246
+ const allOrgs = await sso.platform.organizations.list({
247
+ status: 'pending',
248
+ page: 1,
249
+ limit: 50
250
+ });
251
+
252
+ // Approve organization
253
+ await sso.platform.organizations.approve('org-id', {
254
+ tier_id: 'tier-starter'
255
+ });
256
+
257
+ // Reject organization
258
+ await sso.platform.organizations.reject('org-id', {
259
+ reason: 'Does not meet requirements'
260
+ });
261
+
262
+ // Suspend/activate
263
+ await sso.platform.organizations.suspend('org-id');
264
+ await sso.platform.organizations.activate('org-id');
265
+
266
+ // Update tier
267
+ await sso.platform.organizations.updateTier('org-id', {
268
+ tier_id: 'tier-pro',
269
+ max_services: 20
270
+ });
271
+
272
+ // Promote platform owner
273
+ await sso.platform.promoteOwner({
274
+ user_id: 'user-uuid-here'
275
+ });
276
+
277
+ // Get audit log
278
+ const logs = await sso.platform.getAuditLog({
279
+ action: 'organization.approved',
280
+ limit: 100
281
+ });
282
+ ```
283
+
284
+ ## Error Handling
285
+
286
+ The SDK throws `SsoApiError` for all API errors:
287
+
288
+ ```typescript
289
+ import { SsoApiError } from '@drmhse/sso-sdk';
290
+
291
+ try {
292
+ await sso.organizations.get('non-existent');
293
+ } catch (error) {
294
+ if (error instanceof SsoApiError) {
295
+ console.error(`Error ${error.statusCode}: ${error.message}`);
296
+ console.error(`Code: ${error.errorCode}`);
297
+ console.error(`Timestamp: ${error.timestamp}`);
298
+
299
+ // Utility methods
300
+ if (error.isAuthError()) {
301
+ // Redirect to login
302
+ }
303
+ if (error.isNotFound()) {
304
+ // Show 404 page
305
+ }
306
+ if (error.is('SERVICE_LIMIT_EXCEEDED')) {
307
+ // Handle specific error
308
+ }
309
+ }
310
+ }
311
+ ```
312
+
313
+ ## Framework Integration Examples
314
+
315
+ ### Vue 3 + Pinia
316
+
317
+ ```typescript
318
+ // stores/auth.ts
319
+ import { defineStore } from 'pinia';
320
+ import { SsoClient } from '@drmhse/sso-sdk';
321
+
322
+ export const useAuthStore = defineStore('auth', {
323
+ state: () => ({
324
+ token: localStorage.getItem('jwt'),
325
+ user: null
326
+ }),
327
+
328
+ actions: {
329
+ async login(token: string) {
330
+ this.token = token;
331
+ localStorage.setItem('jwt', token);
332
+ sso.setAuthToken(token);
333
+ await this.fetchUser();
334
+ },
335
+
336
+ async logout() {
337
+ await sso.auth.logout();
338
+ this.token = null;
339
+ this.user = null;
340
+ localStorage.removeItem('jwt');
341
+ sso.setAuthToken(null);
342
+ },
343
+
344
+ async fetchUser() {
345
+ this.user = await sso.user.getProfile();
346
+ }
347
+ }
348
+ });
349
+
350
+ // Global SSO instance
351
+ export const sso = new SsoClient({
352
+ baseURL: import.meta.env.VITE_SSO_URL,
353
+ token: localStorage.getItem('jwt')
354
+ });
355
+ ```
356
+
357
+ ### React + Context
358
+
359
+ ```typescript
360
+ // SsoContext.tsx
361
+ import { createContext, useContext } from 'react';
362
+ import { SsoClient } from '@drmhse/sso-sdk';
363
+
364
+ const sso = new SsoClient({
365
+ baseURL: process.env.REACT_APP_SSO_URL,
366
+ token: localStorage.getItem('jwt')
367
+ });
368
+
369
+ const SsoContext = createContext(sso);
370
+
371
+ export const useSso = () => useContext(SsoContext);
372
+
373
+ export const SsoProvider = ({ children }) => (
374
+ <SsoContext.Provider value={sso}>
375
+ {children}
376
+ </SsoContext.Provider>
377
+ );
378
+ ```
379
+
380
+ ## TypeScript
381
+
382
+ The SDK is written in TypeScript and includes complete type definitions. All types are exported:
383
+
384
+ ```typescript
385
+ import type {
386
+ Organization,
387
+ Service,
388
+ User,
389
+ JwtClaims,
390
+ OAuthProvider
391
+ } from '@drmhse/sso-sdk';
392
+ ```
393
+
394
+ ## License
395
+
396
+ MIT
397
+
398
+ ## Contributing
399
+
400
+ Contributions are welcome! Please open an issue or pull request.