@mission_sciences/provider-sdk 0.1.2 → 0.2.0-dev.03bcad5

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
@@ -7,295 +7,381 @@
7
7
  [![npm version](https://img.shields.io/npm/v/@mission_sciences/provider-sdk)](https://www.npmjs.com/package/@mission_sciences/provider-sdk)
8
8
  [![GitHub Actions](https://github.com/Mission-Sciences/provider-sdk/workflows/Publish%20Package/badge.svg)](https://github.com/Mission-Sciences/provider-sdk/actions)
9
9
 
10
- > **📦 Migration Complete**: This package has been migrated from Bitbucket to GitHub and renamed from `@marketplace/provider-sdk` to `@mission_sciences/provider-sdk`. Now available on public npm with cryptographic provenance! See [Migration Guide](#-migration-from-marketplaceprovider-sdk) below.
11
-
12
- ## 🚀 Quick Start
10
+ ## Quick Start
13
11
 
14
12
  ```bash
15
- # Install
16
13
  npm install @mission_sciences/provider-sdk
14
+ ```
17
15
 
18
- # Initialize
19
- import MarketplaceSDK from '@mission_sciences/provider-sdk';
16
+ ```javascript
17
+ import { MarketplaceSDK } from '@mission_sciences/provider-sdk';
20
18
 
21
19
  const sdk = new MarketplaceSDK({
22
- jwtParamName: 'jwt',
20
+ jwksUri: 'https://api.generalwisdom.com/.well-known/jwks.json',
23
21
  applicationId: 'your-app-id',
24
- jwksUrl: 'https://api.generalwisdom.com/.well-known/jwks.json',
25
- onSessionStart: async (context) => {
26
- // Your auth logic
27
- },
28
- onSessionEnd: async (context) => {
29
- // Cleanup logic
22
+ autoStart: true,
23
+ hooks: {
24
+ async onSessionStart(context) {
25
+ // context.jwt - raw JWT string
26
+ // context.userId - GW user ID
27
+ // context.email - user email
28
+ // context.sessionId, context.orgId, context.applicationId, etc.
29
+
30
+ // Exchange the GW JWT for your app's auth tokens
31
+ const res = await fetch('/auth/marketplace', {
32
+ method: 'POST',
33
+ headers: { 'Content-Type': 'application/json' },
34
+ body: JSON.stringify({ jwt: context.jwt }),
35
+ });
36
+ const { token } = await res.json();
37
+ localStorage.setItem('auth_token', token);
38
+ },
39
+ async onSessionEnd(context) {
40
+ // context.reason = 'expired' | 'manual' | 'error'
41
+ localStorage.clear();
42
+ },
30
43
  },
31
44
  });
32
45
 
33
46
  await sdk.initialize();
34
47
  ```
35
48
 
36
- ## 📚 Documentation
49
+ ## Documentation
37
50
 
38
- - **[Integration Guide](./INTEGRATION_GUIDE.md)** - Comprehensive guide for all frameworks
39
- - **[Quick Start Guide](./QUICKSTART.md)** - Get started in 3 minutes
40
- - **[Testing Guide](./TESTING_GUIDE.md)** - Testing strategies
41
- - **[JWT Specification](./jwt-specification.md)** - Token format details
51
+ - **[Integration Guide](./INTEGRATION_GUIDE.md)** -- Comprehensive guide for all frameworks (vanilla JS, React, Vue, Chrome Extensions)
52
+ - **[Quick Start Guide](./QUICKSTART.md)** -- Get started in 3 minutes
53
+ - **[JWT Specification](./jwt-specification.md)** -- Token format and claim details
54
+ - **[Validation Guide](./VALIDATION.md)** -- Testing and validation strategies
55
+ - **[Auth Integration Demo](./examples/auth-integration/)** -- Full working demo with 5 identity providers and 2 frontend implementations
42
56
 
43
- ### Example Integrations
57
+ ## Features
44
58
 
45
- - **[GhostDog Integration](../extension-ghostdog/MARKETPLACE_INTEGRATION.md)** - Real-world Chrome extension example
59
+ ### Core
60
+ - **Zero Config**: Extracts JWT from URL, validates via JWKS, starts session timer
61
+ - **Framework Agnostic**: Works with vanilla JS, React, Vue, or any framework
62
+ - **TypeScript First**: Full type definitions with all interfaces exported
63
+ - **Lightweight**: Single dependency (`jose` for JWT/JWKS)
64
+ - **Secure**: RS256 JWT verification with JWKS rotation support
46
65
 
47
- ## Features
66
+ ### Lifecycle Hooks
67
+ - **`onSessionStart`** -- Fires after JWT validation, before timer starts. Use to exchange tokens with your auth system. Hook failure prevents session start (strict mode).
68
+ - **`onSessionEnd`** -- Fires on expiration or manual end. Use to revoke sessions. Errors are logged but don't block teardown (lenient mode).
69
+ - **`onSessionWarning`** -- Fires when session nears expiration (configurable threshold).
70
+ - **`onSessionExtend`** -- Fires after session extension. Use to refresh auth tokens.
48
71
 
49
- ### Core Features
50
- - **Zero Dependencies**: Self-contained with minimal external deps
51
- - **Framework Agnostic**: Works with vanilla JS, React, Vue, and more
52
- - **TypeScript First**: Full type definitions included
53
- - **Lightweight**: < 10KB gzipped
54
- - **Secure**: RS256 JWT verification with JWKS
55
- - **Customizable**: Flexible styling and event handling
72
+ ### Advanced (Phase 2)
73
+ - **Heartbeat**: Automatic server sync at configurable intervals
74
+ - **Multi-Tab Sync**: Master tab election via BroadcastChannel API
75
+ - **Session Extension**: Self-service renewal with `extendSession(minutes)`
76
+ - **Early Completion**: End sessions early with `completeSession(actualMinutes)`
77
+ - **Visibility API**: Auto-pause timer when tab is hidden
78
+ - **Backend Validation**: Alternative to JWKS for sensitive apps
56
79
 
57
- ### Advanced Features (Phase 2)
58
- - ❤️ **Heartbeat System**: Automatic server sync
59
- - 🔄 **Multi-Tab Sync**: Master tab election with BroadcastChannel API
60
- - ⏰ **Session Extension**: Self-service renewal
61
- - ✅ **Early Completion**: End sessions early with refund calculation
62
- - 👁️ **Visibility API**: Auto-pause when tab hidden
63
- - 🔐 **Backend Validation**: Alternative to JWKS for sensitive apps
64
-
65
- ## 🎯 How It Works
80
+ ## How It Works
66
81
 
67
82
  ```
68
- Marketplace JWT in URL SDK validates Your app authenticates Session active
83
+ Marketplace --> JWT in URL --> SDK validates via JWKS --> Lifecycle hooks fire --> Session active
69
84
  ```
70
85
 
71
- When users launch your app from the marketplace:
72
-
73
- 1. **URL Detection**: SDK checks for JWT parameter (`?jwt=...`)
74
- 2. **JWT Validation**: Verifies signature using RS256 and JWKS
75
- 3. **Session Start**: Calls your `onSessionStart` hook
76
- 4. **Timer Start**: Countdown begins
77
- 5. **Session Active**: User interacts with your app
78
- 6. **Warning**: Alert at 5 minutes remaining (configurable)
79
- 7. **Session End**: Calls your `onSessionEnd` hook
80
- 8. **Redirect**: Returns to marketplace (optional)
81
-
82
- ## 🔒 Secure Publishing & Provenance
86
+ 1. User launches your app from the marketplace with `?gwSession=<token>` in the URL (parameter name configurable via `jwtParamName`)
87
+ 2. SDK extracts the JWT and verifies it against the JWKS endpoint (RS256)
88
+ 3. `hooks.onSessionStart` fires with the validated session context
89
+ 4. Session timer starts counting down
90
+ 5. `hooks.onSessionWarning` fires at the configured threshold
91
+ 6. When the timer expires or `endSession()` is called, `hooks.onSessionEnd` fires
83
92
 
84
- This package is published with cryptographic provenance attestation:
85
-
86
- - **Dual Publishing**: Available on both [npm](https://www.npmjs.com/package/@mission_sciences/provider-sdk) (public) and AWS CodeArtifact (private)
87
- - **Cryptographic Signatures**: All releases signed with GitHub Actions OIDC
88
- - **Provenance Transparency**: Build provenance recorded in [Sigstore transparency log](https://search.sigstore.dev)
89
- - **No Hardcoded Secrets**: CI/CD uses OIDC for AWS and npm authentication
90
- - **Automated CI/CD**: GitHub Actions workflow with comprehensive testing and security checks
93
+ ## Installation
91
94
 
92
- Verify package provenance:
93
95
  ```bash
94
- npm view @mission_sciences/provider-sdk --json | jq .dist
95
- ```
96
-
97
- ## 📦 Installation
96
+ # npm (public registry)
97
+ npm install @mission_sciences/provider-sdk
98
98
 
99
- ### NPM (Public Registry)
99
+ # yarn
100
+ yarn add @mission_sciences/provider-sdk
100
101
 
101
- ```bash
102
- npm install @mission_sciences/provider-sdk
102
+ # pnpm
103
+ pnpm add @mission_sciences/provider-sdk
103
104
  ```
104
105
 
105
106
  ### AWS CodeArtifact (Private Registry)
106
107
 
107
108
  ```bash
108
- # Configure CodeArtifact
109
109
  aws codeartifact login \
110
110
  --tool npm \
111
111
  --domain general-wisdom-dev \
112
112
  --repository sdk-packages \
113
113
  --region us-east-1
114
114
 
115
- # Install
116
115
  npm install @mission_sciences/provider-sdk
117
116
  ```
118
117
 
119
- ### Yarn
120
-
121
- ```bash
122
- yarn add @mission_sciences/provider-sdk
123
- ```
124
-
125
- ### PNPM
126
-
127
- ```bash
128
- pnpm add @mission_sciences/provider-sdk
129
- ```
130
-
131
- ## 🏗️ Basic Usage
118
+ ## Usage
132
119
 
133
120
  ### Vanilla JavaScript
134
121
 
135
122
  ```javascript
136
- import MarketplaceSDK from '@mission_sciences/provider-sdk';
123
+ import { MarketplaceSDK } from '@mission_sciences/provider-sdk';
137
124
 
138
125
  const sdk = new MarketplaceSDK({
139
- jwtParamName: 'jwt',
126
+ jwksUri: '/.well-known/jwks.json',
140
127
  applicationId: 'my-app',
141
- jwksUrl: 'https://api.generalwisdom.com/.well-known/jwks.json',
142
-
143
- onSessionStart: async (context) => {
144
- // Store user info
145
- localStorage.setItem('user_id', context.userId);
146
- localStorage.setItem('session_id', context.sessionId);
147
-
148
- // Show app UI
149
- document.getElementById('app').style.display = 'block';
150
- },
151
-
152
- onSessionEnd: async (context) => {
153
- // Clear storage
154
- localStorage.clear();
155
-
156
- // Hide app UI
157
- document.getElementById('app').style.display = 'none';
128
+ autoStart: true,
129
+ warningThresholdSeconds: 120,
130
+ hooks: {
131
+ async onSessionStart(context) {
132
+ // Exchange GW JWT for your app's tokens
133
+ const res = await fetch('/auth/exchange', {
134
+ method: 'POST',
135
+ headers: { 'Content-Type': 'application/json' },
136
+ body: JSON.stringify({ jwt: context.jwt }),
137
+ });
138
+ const data = await res.json();
139
+ sessionStorage.setItem('access_token', data.access_token);
140
+ },
141
+ async onSessionEnd(context) {
142
+ sessionStorage.clear();
143
+ },
158
144
  },
159
145
  });
160
146
 
147
+ // Event handlers (separate from hooks -- these fire after hooks complete)
148
+ sdk.on('onSessionStart', (sessionData) => {
149
+ document.getElementById('app').style.display = 'block';
150
+ });
151
+
152
+ sdk.on('onSessionEnd', () => {
153
+ document.getElementById('app').style.display = 'none';
154
+ });
155
+
156
+ sdk.on('onError', (error) => {
157
+ console.error('SDK error:', error.message);
158
+ });
159
+
161
160
  await sdk.initialize();
162
161
 
163
- // Mount session header
164
- const header = sdk.createSessionHeader();
165
- header.mount('#session-header');
162
+ // Timer display
163
+ setInterval(() => {
164
+ document.getElementById('timer').textContent = sdk.getFormattedTime();
165
+ }, 1000);
166
166
  ```
167
167
 
168
168
  ### React
169
169
 
170
170
  ```typescript
171
- import { useEffect } from 'react';
172
- import MarketplaceSDK from '@mission_sciences/provider-sdk';
171
+ import { useEffect, useState, useRef, useCallback } from 'react';
172
+ import { MarketplaceSDK } from '@mission_sciences/provider-sdk';
173
173
 
174
174
  function useMarketplaceSession() {
175
+ const [session, setSession] = useState(null);
176
+ const [loading, setLoading] = useState(true);
177
+ const [time, setTime] = useState('--:--');
178
+ const sdkRef = useRef(null);
179
+
175
180
  useEffect(() => {
176
181
  const sdk = new MarketplaceSDK({
177
- jwtParamName: 'jwt',
182
+ jwksUri: '/.well-known/jwks.json',
178
183
  applicationId: 'my-react-app',
179
- jwksUrl: 'https://api.generalwisdom.com/.well-known/jwks.json',
180
-
181
- onSessionStart: async (context) => {
182
- // Call your auth API
183
- await authenticateUser(context);
184
- },
185
-
186
- onSessionEnd: async (context) => {
187
- // Clear auth state
188
- await logout();
184
+ autoStart: true,
185
+ hooks: {
186
+ async onSessionStart(context) {
187
+ await authenticateUser(context.jwt);
188
+ },
189
+ async onSessionEnd(context) {
190
+ await logout();
191
+ },
189
192
  },
190
193
  });
191
194
 
195
+ sdk.on('onSessionStart', (data) => {
196
+ setSession(data);
197
+ setLoading(false);
198
+ });
199
+
200
+ sdk.on('onError', (err) => {
201
+ console.error(err);
202
+ setLoading(false);
203
+ });
204
+
205
+ sdkRef.current = sdk;
192
206
  sdk.initialize();
193
207
 
194
- return () => sdk.destroy();
208
+ const interval = setInterval(() => {
209
+ if (sdkRef.current) setTime(sdkRef.current.getFormattedTime());
210
+ }, 1000);
211
+
212
+ return () => {
213
+ clearInterval(interval);
214
+ sdk.destroy();
215
+ };
195
216
  }, []);
217
+
218
+ return { session, loading, time, sdk: sdkRef };
196
219
  }
197
220
  ```
198
221
 
199
- See [INTEGRATION_GUIDE.md](./INTEGRATION_GUIDE.md) for Vue, Chrome Extensions, and more.
222
+ See the [Integration Guide](./INTEGRATION_GUIDE.md) for Vue, Chrome Extensions, and more patterns.
200
223
 
201
- ## 🎨 Session Header Component
224
+ ### Auth Integration Demo
202
225
 
203
- Pre-built UI component for displaying session timer:
226
+ A complete working example with Docker, 5 identity providers, and 2 frontend implementations lives in `examples/auth-integration/`:
204
227
 
205
- ```typescript
206
- const header = sdk.createSessionHeader({
207
- containerId: 'session-header',
208
- theme: 'dark', // 'light' | 'dark' | 'auto'
209
- showControls: true,
210
- showEndButton: true,
211
- });
228
+ ```bash
229
+ # Build the SDK first
230
+ npm run build
231
+
232
+ # Start the demo (defaults to mock IdP + React storefront + Auth0 protocol)
233
+ cd examples/auth-integration
234
+ cp .env.mock .env
235
+ docker compose up --build
212
236
 
213
- header.mount('#session-header');
237
+ # Open http://localhost:8080/generate-test-url
214
238
  ```
215
239
 
216
- **Custom Styling**:
240
+ The demo includes:
241
+ - **Mock IdP** that speaks Auth0, Okta, Azure AD, and Cognito protocols
242
+ - **React ecommerce storefront** with product shop, cart, role-gated admin panel
243
+ - **Vanilla JS reference** with auth hooks and event log
244
+ - **Backend** with provider-agnostic auth exchange using the `AuthProvider` interface
245
+ - **Role-based access control** showing `gw-user` vs `org-admin` UI gating
217
246
 
218
- ```css
219
- .gw-session-header {
220
- background: #1a1a1a;
221
- padding: 12px 24px;
222
- }
247
+ See [examples/auth-integration/README.md](./examples/auth-integration/README.md) for full documentation, and [examples/auth-integration/docs/DEMO_WALKTHROUGH.md](./examples/auth-integration/docs/DEMO_WALKTHROUGH.md) for a step-by-step walkthrough.
223
248
 
224
- .gw-session-timer {
225
- font-size: 18px;
226
- color: #00ff88;
227
- }
249
+ ## Configuration
228
250
 
229
- .gw-session-timer--warning {
230
- color: #ff6b00;
251
+ ```typescript
252
+ interface SDKConfig {
253
+ // JWT & Validation
254
+ jwksUri?: string; // JWKS endpoint (default: GW production endpoint)
255
+ jwtParamName?: string; // URL query parameter name (default: 'gwSession')
256
+ applicationId?: string; // Your application ID
257
+ useBackendValidation?: boolean; // Use backend instead of JWKS (default: false)
258
+
259
+ // Session Behavior
260
+ autoStart?: boolean; // Auto-start from URL JWT (default: true)
261
+ warningThresholdSeconds?: number; // Warning before expiry (default: 300)
262
+ marketplaceUrl?: string; // Redirect URL after session end
263
+
264
+ // Lifecycle Hooks
265
+ hooks?: {
266
+ onSessionStart?: (ctx: SessionStartContext) => Promise<void> | void;
267
+ onSessionEnd?: (ctx: SessionEndContext) => Promise<void> | void;
268
+ onSessionExtend?: (ctx: SessionExtendContext) => Promise<void> | void;
269
+ onSessionWarning?: (ctx: SessionWarningContext) => Promise<void> | void;
270
+ };
271
+ hookTimeoutMs?: number; // Hook timeout (default: 5000)
272
+
273
+ // Phase 2 Features
274
+ enableHeartbeat?: boolean; // Server heartbeat (default: false)
275
+ heartbeatIntervalSeconds?: number; // Heartbeat interval (default: 30)
276
+ enableTabSync?: boolean; // Multi-tab sync (default: false)
277
+ pauseOnHidden?: boolean; // Pause when tab hidden (default: false)
278
+
279
+ // UI
280
+ themeMode?: 'light' | 'dark' | 'auto';
281
+ debug?: boolean; // Console logging (default: false)
231
282
  }
232
283
  ```
233
284
 
234
- ## 🔐 Authentication Integration
285
+ ## API Reference
235
286
 
236
- ### Exchange JWT for Your App's Tokens
287
+ ### MarketplaceSDK
237
288
 
238
289
  ```typescript
239
- onSessionStart: async (context) => {
240
- // Send marketplace JWT to your backend
241
- const response = await fetch('https://api.your-app.com/auth/marketplace', {
242
- method: 'POST',
243
- headers: {
244
- 'Authorization': `Bearer ${context.jwt}`,
245
- },
246
- });
247
-
248
- const { token } = await response.json();
249
-
250
- // Store your app's auth token
251
- localStorage.setItem('auth_token', token);
252
- },
253
- ```
290
+ class MarketplaceSDK {
291
+ constructor(config: SDKConfig)
254
292
 
255
- **Backend Example** (Python/Flask):
256
-
257
- ```python
258
- @app.route('/auth/marketplace', methods=['POST'])
259
- def marketplace_auth():
260
- jwt_token = request.headers.get('Authorization').replace('Bearer ', '')
261
-
262
- # Validate JWT
263
- claims = validate_marketplace_jwt(jwt_token)
264
-
265
- # Create/update user
266
- user = get_or_create_user(claims['userId'], claims.get('email'))
267
-
268
- # Generate your app's token
269
- app_token = generate_app_token(user)
270
-
271
- return jsonify({'token': app_token})
272
- ```
293
+ // Initialization
294
+ async initialize(): Promise<SessionData>
273
295
 
274
- See [INTEGRATION_GUIDE.md#authentication-integration](./INTEGRATION_GUIDE.md#authentication-integration) for Cognito, Firebase, Auth0 examples.
296
+ // Event handlers (fire after hooks complete)
297
+ on(event: 'onSessionStart', handler: (data: SessionData) => void): void
298
+ on(event: 'onSessionEnd', handler: () => void): void
299
+ on(event: 'onSessionWarning', handler: (data: { remainingSeconds: number }) => void): void
300
+ on(event: 'onError', handler: (error: Error) => void): void
275
301
 
276
- ## ⚙️ Configuration
302
+ // Timer
303
+ startTimer(): void
304
+ pauseTimer(): void
305
+ resumeTimer(): void
306
+ isTimerRunning(): boolean
307
+ getRemainingTime(): number // Seconds remaining
308
+ getFormattedTime(): string // "M:SS" format
309
+ getFormattedTimeWithHours(): string // "H:MM:SS" format
277
310
 
278
- ### SDK Options
311
+ // Session control
312
+ async endSession(): Promise<void>
313
+ async extendSession(additionalMinutes: number): Promise<void>
314
+ async completeSession(actualUsageMinutes?: number): Promise<void>
315
+
316
+ // Data
317
+ getSessionData(): SessionData | null
318
+
319
+ // Cleanup
320
+ destroy(): void
321
+ }
322
+ ```
323
+
324
+ ### Context Types
279
325
 
280
326
  ```typescript
281
- interface SDKOptions {
282
- // Required
283
- jwtParamName: string; // URL parameter name
284
- applicationId: string; // Your app ID
285
- jwksUrl: string; // JWKS endpoint
286
- onSessionStart: (context) => Promise<void>;
287
- onSessionEnd: (context) => Promise<void>;
288
-
289
- // Optional
290
- onSessionWarning?: (context) => Promise<void>;
291
- onSessionExtend?: (context) => Promise<void>;
292
- marketplaceUrl?: string; // Redirect after session end
293
- warningThresholdMinutes?: number; // Default: 5
294
- debug?: boolean; // Enable logging
295
- pauseWhenHidden?: boolean; // Auto-pause when tab hidden
327
+ interface SessionStartContext {
328
+ sessionId: string;
329
+ userId: string;
330
+ email?: string;
331
+ orgId: string;
332
+ applicationId: string;
333
+ durationMinutes: number;
334
+ expiresAt: number; // Unix seconds
335
+ jwt: string; // Raw JWT for backend exchange
336
+ }
337
+
338
+ interface SessionEndContext {
339
+ sessionId: string;
340
+ userId: string;
341
+ reason: 'expired' | 'manual' | 'error';
342
+ actualDurationMinutes?: number;
343
+ }
344
+
345
+ interface SessionExtendContext {
346
+ sessionId: string;
347
+ userId: string;
348
+ additionalMinutes: number;
349
+ newExpiresAt: number;
350
+ }
351
+
352
+ interface SessionWarningContext {
353
+ sessionId: string;
354
+ userId: string;
355
+ remainingSeconds: number;
296
356
  }
297
357
  ```
298
358
 
359
+ ### Exports
360
+
361
+ ```typescript
362
+ // Main class
363
+ export { MarketplaceSDK } from '@mission_sciences/provider-sdk';
364
+ export { MarketplaceSDK as default } from '@mission_sciences/provider-sdk';
365
+
366
+ // UI components
367
+ export { SessionHeader } from '@mission_sciences/provider-sdk';
368
+ export { WarningModal } from '@mission_sciences/provider-sdk';
369
+
370
+ // Core utilities
371
+ export { JWTParser, JWKSValidator, TimerManager } from '@mission_sciences/provider-sdk';
372
+ export { HeartbeatManager, TabSyncManager } from '@mission_sciences/provider-sdk';
373
+
374
+ // Theming
375
+ export { lightTheme, darkTheme, getTheme, generateCSSVariables } from '@mission_sciences/provider-sdk';
376
+
377
+ // Types
378
+ export type {
379
+ SDKConfig, SessionData, SDKEvents,
380
+ SessionStartContext, SessionEndContext, SessionExtendContext, SessionWarningContext,
381
+ SessionLifecycleHooks, ThemeMode,
382
+ } from '@mission_sciences/provider-sdk';
383
+ ```
384
+
299
385
  ### JWT Structure
300
386
 
301
387
  ```json
@@ -308,11 +394,15 @@ interface SDKOptions {
308
394
  "startTime": 1763599337,
309
395
  "durationMinutes": 60,
310
396
  "exp": 1763602937,
311
- "iat": 1763599337
397
+ "iat": 1763599337,
398
+ "iss": "generalwisdom.com",
399
+ "sub": "a47884c8-50d1-7040-2de8-b7801699643c"
312
400
  }
313
401
  ```
314
402
 
315
- ## 🧪 Testing
403
+ > **Note:** `email` is optional. It is included when available from the user's identity provider but may not be present in all JWTs. Always check for its presence before using it.
404
+
405
+ ## Testing
316
406
 
317
407
  ### Generate Test JWT
318
408
 
@@ -321,278 +411,137 @@ npm run generate-keys # Create RSA key pair (dev only)
321
411
  npm run generate-jwt 60 # Generate 60-minute JWT
322
412
  ```
323
413
 
324
- ### Test Server
414
+ ### Dev Server
325
415
 
326
416
  ```bash
327
417
  npm run test-server # Start dev server at localhost:3000
418
+ npm run test-server-p2 # Phase 2 dev server with heartbeat/tab-sync
328
419
  ```
329
420
 
330
- Open: `http://localhost:3000?jwt=<YOUR_JWT>`
421
+ Open: `http://localhost:3000?gwSession=<YOUR_JWT>`
331
422
 
332
- See [TESTING_GUIDE.md](./TESTING_GUIDE.md) for unit, integration, and E2E testing.
333
-
334
- ## 🛠️ Development
335
-
336
- ### Build
423
+ ### Unit Tests
337
424
 
338
425
  ```bash
339
- npm run build # Build for production
340
- npm run dev # Watch mode
426
+ npm run test # Run all tests
427
+ npm run test:watch # Watch mode
428
+ npm run test:coverage # Coverage report
429
+ npm run test:integration # Integration tests
341
430
  ```
342
431
 
343
- ### Code Quality
432
+ ## Development
344
433
 
345
434
  ```bash
435
+ npm run build # Build for production (tsc + vite)
436
+ npm run dev # Vite dev server with HMR
346
437
  npm run lint # ESLint
347
438
  npm run format # Prettier
348
- npm run type-check # TypeScript
349
439
  ```
350
440
 
351
- ### Examples
441
+ ### Build Output
352
442
 
353
- ```bash
354
- cd examples/vanilla-js
355
- npm install
356
- npm run dev
443
+ ```
444
+ dist/
445
+ ├── marketplace-sdk.es.js # ESM bundle
446
+ ├── marketplace-sdk.es.js.map
447
+ ├── marketplace-sdk.umd.js # UMD bundle
448
+ ├── marketplace-sdk.umd.js.map
449
+ └── index.d.ts # TypeScript declarations
357
450
  ```
358
451
 
359
- ## 🏗️ Infrastructure & CI/CD
360
-
361
- ### GitHub Actions Workflow
362
-
363
- The package is built and published using a comprehensive 8-job GitHub Actions pipeline:
364
-
365
- 1. **Test & Build** - Unit tests, type checking, linting, and production build
366
- 2. **Terraform Plan** - Review infrastructure changes (CodeArtifact setup)
367
- 3. **Terraform Apply** - Create/update AWS infrastructure
368
- 4. **Publish CodeArtifact** - Publish to private AWS registry
369
- 5. **Verify CodeArtifact** - Confirm successful publication
370
- 6. **Publish npm** - Publish to public npm with provenance
371
- 7. **Verify npm** - Confirm successful publication
372
- 8. **Create Release** - Generate GitHub release with artifacts
373
-
374
- **Authentication:**
375
- - AWS: OIDC via IAM role `GitHubActions-ProviderSDK` (no access keys)
376
- - npm: Trusted Publishing with cryptographic provenance (no tokens)
377
-
378
- ### Planning Documentation
379
-
380
- Comprehensive migration and setup documentation available in `planning/`:
381
-
382
- - **[PROJECT_CONTEXT.md](./planning/PROJECT_CONTEXT.md)** - Project overview and context
383
- - **[EXISTING_ANALYSIS.md](./planning/EXISTING_ANALYSIS.md)** - Codebase analysis
384
- - **[REQUIREMENTS.md](./planning/REQUIREMENTS.md)** - Migration requirements
385
- - **[CI_CD_ARCHITECTURE.md](./planning/CI_CD_ARCHITECTURE.md)** - Workflow design
386
- - **[AWS_OIDC_SETUP.md](./planning/AWS_OIDC_SETUP.md)** - AWS OIDC configuration
387
- - **[NPM_TRUSTED_PUBLISHING_SETUP.md](./planning/NPM_TRUSTED_PUBLISHING_SETUP.md)** - npm provenance setup
388
- - **[GITHUB_SETUP_GUIDE.md](./planning/GITHUB_SETUP_GUIDE.md)** - Complete setup guide
389
- - **[MIGRATION_CHECKLIST.md](./planning/MIGRATION_CHECKLIST.md)** - Migration checklist
452
+ ## Infrastructure & CI/CD
390
453
 
391
- ## 📖 API Reference
454
+ The package is built and published using an 8-job GitHub Actions pipeline:
392
455
 
393
- ### MarketplaceSDK
456
+ 1. **Test & Build** -- Unit tests, type checking, linting, production build
457
+ 2. **Terraform Plan** -- Review infrastructure changes (CodeArtifact)
458
+ 3. **Terraform Apply** -- Create/update AWS infrastructure
459
+ 4. **Publish CodeArtifact** -- Publish to private AWS registry
460
+ 5. **Verify CodeArtifact** -- Confirm publication
461
+ 6. **Publish npm** -- Publish to public npm with provenance
462
+ 7. **Verify npm** -- Confirm publication
463
+ 8. **Create Release** -- GitHub release with artifacts
394
464
 
395
- ```typescript
396
- class MarketplaceSDK {
397
- constructor(options: SDKOptions)
398
-
399
- // Initialize SDK
400
- async initialize(): Promise<void>
401
-
402
- // Session management
403
- hasActiveSession(): boolean
404
- getSession(): Session | null
405
- async endSession(reason: string): Promise<void>
406
- async extendSession(minutes: number): Promise<void>
407
-
408
- // UI components
409
- createSessionHeader(options?: HeaderOptions): SessionHeader
410
-
411
- // Timer control
412
- pauseTimer(): void
413
- resumeTimer(): void
414
- isTimerPaused(): boolean
415
-
416
- // Cleanup
417
- destroy(): void
418
- }
419
- ```
465
+ **Authentication**: AWS via OIDC (no access keys), npm via Trusted Publishing with cryptographic provenance (no tokens).
420
466
 
421
- ### Context Types
467
+ ## Secure Publishing & Provenance
422
468
 
423
- ```typescript
424
- interface SessionStartContext {
425
- sessionId: string;
426
- applicationId: string;
427
- userId: string;
428
- orgId?: string;
429
- email?: string;
430
- startTime: number;
431
- expiresAt: number;
432
- durationMinutes: number;
433
- jwt: string;
434
- }
469
+ - **Dual Publishing**: [npm](https://www.npmjs.com/package/@mission_sciences/provider-sdk) (public) + AWS CodeArtifact (private)
470
+ - **Cryptographic Signatures**: All releases signed with GitHub Actions OIDC
471
+ - **Provenance Transparency**: Build provenance in [Sigstore transparency log](https://search.sigstore.dev)
472
+ - **No Hardcoded Secrets**: CI/CD uses OIDC for all authentication
435
473
 
436
- interface SessionEndContext {
437
- sessionId: string;
438
- userId: string;
439
- reason: 'expired' | 'manual' | 'error';
440
- actualDurationMinutes: number;
441
- }
474
+ ```bash
475
+ # Verify provenance
476
+ npm view @mission_sciences/provider-sdk --json | jq .dist
442
477
  ```
443
478
 
444
- See [INTEGRATION_GUIDE.md#api-reference](./INTEGRATION_GUIDE.md#api-reference) for complete API documentation.
445
-
446
- ## 🚀 Production Deployment
479
+ ## Production Checklist
447
480
 
448
- ### Checklist
449
-
450
- - [ ] Update `jwksUrl` to production endpoint
481
+ - [ ] Set `jwksUri` to production JWKS endpoint
451
482
  - [ ] Set correct `applicationId`
452
- - [ ] Enable HTTPS for all endpoints
453
- - [ ] Configure proper CORS headers
454
- - [ ] Set up secrets management
455
- - [ ] Enable rate limiting
456
- - [ ] Configure monitoring and logging
457
- - [ ] Test with production JWT
458
- - [ ] Load test auth endpoints
459
-
460
- See [INTEGRATION_GUIDE.md#production-deployment](./INTEGRATION_GUIDE.md#production-deployment) for complete checklist.
483
+ - [ ] Set `hookTimeoutMs` appropriately for your auth provider latency
484
+ - [ ] Enable HTTPS on all endpoints
485
+ - [ ] Configure CORS headers
486
+ - [ ] Set up secrets management for backend token exchange
487
+ - [ ] Enable rate limiting on auth endpoints
488
+ - [ ] Verify tokens server-side (not just client-side JWKS)
489
+ - [ ] Test with production JWTs
461
490
 
462
- ## 🐛 Troubleshooting
491
+ ## Troubleshooting
463
492
 
464
- ### Common Issues
493
+ **"No token found in URL parameter 'gwSession' or storage"** -- The SDK looks for the JWT in the URL query parameter configured via `jwtParamName` (default: `gwSession`). Make sure the marketplace redirect includes the JWT as `?gwSession=<token>` (or your custom parameter name).
465
494
 
466
- **JWT validation failed**
467
- - Check JWKS URL is correct
468
- - Verify applicationId matches
469
- - Ensure JWT not expired
495
+ **JWT validation failed** -- Check that `jwksUri` points to the correct JWKS endpoint and that the JWT hasn't expired.
470
496
 
471
- **Session header not showing**
472
- - Verify mount element exists
473
- - Check SDK initialized
474
- - Confirm active session
497
+ **Hook timeout** -- The default `hookTimeoutMs` is 5000ms. If your auth exchange involves multiple API calls (user lookup + creation + token grant), increase it to 10000ms or more.
475
498
 
476
- **Auth server 500 error**
477
- - Check Cognito configuration
478
- - Verify client secret (if required)
479
- - Review server logs
499
+ **Session header not rendering** -- `SessionHeader` is a separate exported class, not a method on `MarketplaceSDK`. Import and instantiate it directly.
480
500
 
481
- See [INTEGRATION_GUIDE.md#troubleshooting](./INTEGRATION_GUIDE.md#troubleshooting) for detailed solutions.
501
+ **Multi-tab conflicts** -- Enable `enableTabSync: true` to elect a master tab and sync session state across tabs via BroadcastChannel.
482
502
 
483
- ## 📚 Resources
484
-
485
- - **[Integration Guide](./INTEGRATION_GUIDE.md)** - Complete integration reference
486
- - **[Quick Start](./QUICKSTART.md)** - Get started in 3 minutes
487
- - **[Testing Guide](./TESTING_GUIDE.md)** - Testing strategies
488
- - **[JWT Spec](./jwt-specification.md)** - Token format details
489
- - **[Examples](./examples/)** - Sample implementations
490
- - **[GhostDog Integration](../extension-ghostdog/MARKETPLACE_INTEGRATION.md)** - Real-world example
491
-
492
- ## 📦 Migration from @marketplace/provider-sdk
493
-
494
- ### Repository Migration
495
-
496
- This package has been migrated from Bitbucket to GitHub with enhanced security and public availability:
497
-
498
- **Old:**
499
- - Repository: Bitbucket (private)
500
- - Package: `@marketplace/provider-sdk`
501
- - Registry: AWS CodeArtifact only (private)
502
- - CI/CD: Bitbucket Pipelines with hardcoded credentials
503
-
504
- **New:**
505
- - Repository: [GitHub/Mission-Sciences/provider-sdk](https://github.com/Mission-Sciences/provider-sdk) (public)
506
- - Package: `@mission_sciences/provider-sdk`
507
- - Registry: npm (public) + AWS CodeArtifact (private)
508
- - CI/CD: GitHub Actions with OIDC (zero secrets)
509
- - Security: Cryptographic provenance attestation
510
-
511
- ### Migration Steps
512
-
513
- #### Step 1: Update package.json
503
+ ## Migration from @marketplace/provider-sdk
514
504
 
515
505
  ```bash
506
+ # 1. Update package
516
507
  npm uninstall @marketplace/provider-sdk
517
508
  npm install @mission_sciences/provider-sdk
518
- ```
519
-
520
- #### Step 2: Update imports
521
509
 
522
- ```typescript
523
- // Old
524
- import MarketplaceSDK from '@marketplace/provider-sdk';
525
-
526
- // New
527
- import MarketplaceSDK from '@mission_sciences/provider-sdk';
528
- ```
529
-
530
- #### Step 3: Simplify registry config
531
-
532
- **If using npm (public registry):**
533
- ```bash
534
- # Remove .npmrc - use default npm registry (no configuration needed!)
510
+ # 2. Update imports
511
+ # Old: import MarketplaceSDK from '@marketplace/provider-sdk';
512
+ # New: import { MarketplaceSDK } from '@mission_sciences/provider-sdk';
535
513
  ```
536
514
 
537
- **If using CodeArtifact (private registry):**
538
- ```bash
539
- # Update your .npmrc
540
- @mission_sciences:registry=https://general-wisdom-dev-540845145946.d.codeartifact.us-east-1.amazonaws.com/npm/sdk-packages/
541
- ```
542
-
543
- **Note**: The API is 100% compatible. No code changes required beyond the package name!
544
-
545
- ### Benefits of Migration
515
+ The API is 100% compatible. No code changes required beyond the package name.
546
516
 
547
- **Public Availability**: Install from npm without AWS credentials
548
- **Provenance Attestation**: Cryptographic proof of build integrity
549
- **Enhanced Security**: OIDC authentication, no hardcoded secrets
550
- **Open Source Workflow**: Public CI/CD pipeline on GitHub Actions
551
- **Dual Publishing**: Available on both public npm and private CodeArtifact
517
+ **What changed:**
518
+ - Repository: Bitbucket (private) -> [GitHub](https://github.com/Mission-Sciences/provider-sdk) (public)
519
+ - Package: `@marketplace/provider-sdk` -> `@mission_sciences/provider-sdk`
520
+ - Registry: CodeArtifact only -> npm (public) + CodeArtifact (private)
521
+ - CI/CD: Bitbucket Pipelines -> GitHub Actions with OIDC
522
+ - Security: Added cryptographic provenance attestation
552
523
 
553
- ## 🤝 Contributing
524
+ ## Changelog
554
525
 
555
- Contributions welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) first.
526
+ ### v0.1.2 (2025-01-11) -- Migration Release
527
+ - Migrated from Bitbucket to GitHub
528
+ - Package renamed: `@marketplace/provider-sdk` -> `@mission_sciences/provider-sdk`
529
+ - Added cryptographic provenance attestation
530
+ - Dual publishing: npm (public) + AWS CodeArtifact (private)
531
+ - Zero-secret CI/CD with OIDC authentication
532
+ - Added lifecycle hooks (`onSessionStart`, `onSessionEnd`, `onSessionWarning`, `onSessionExtend`)
533
+ - Added heartbeat, multi-tab sync, session extension, early completion
534
+ - Added auth integration demo with 5 identity providers
556
535
 
557
- ## 📄 License
558
-
559
- MIT License - see [LICENSE](./LICENSE) file for details
560
-
561
- ## 🆘 Support
562
-
563
- - **Issues**: [GitHub Issues](https://github.com/Mission-Sciences/provider-sdk/issues)
564
- - **Email**: support@generalwisdom.com
565
- - **Docs**: [docs.generalwisdom.com](https://docs.generalwisdom.com)
566
-
567
- ## 📊 Changelog
568
-
569
- ### v0.1.2 (2025-01-11) - Migration Release
570
- - 🏗️ Migrated from Bitbucket to GitHub
571
- - 📦 Package renamed: `@marketplace/provider-sdk` → `@mission_sciences/provider-sdk`
572
- - 🔒 Added cryptographic provenance attestation
573
- - ☁️ Dual publishing: npm (public) + AWS CodeArtifact (private)
574
- - 🔐 Zero-secret CI/CD with OIDC authentication
575
- - 📝 Comprehensive migration documentation
576
- - 🚀 GitHub Actions workflow with 8-job pipeline
577
-
578
- ### v0.1.1 (2024) - Pre-Migration
536
+ ### v0.1.1 (2024) -- Pre-Migration
579
537
  - Initial Bitbucket release
580
538
  - CodeArtifact-only distribution
581
- - Bitbucket Pipelines CI/CD
582
539
 
583
- ### v2.0.0 (Planned - Phase 2)
584
- - Heartbeat system
585
- - Multi-tab coordination
586
- - Session extension
587
- - Early completion
588
- - Visibility API integration
540
+ ## License
589
541
 
590
- ### v1.0.0 (Phase 1)
591
- - JWT validation with JWKS
592
- - Session timer management
593
- - Lifecycle hooks
594
- - Session header component
542
+ MIT -- see [LICENSE](./LICENSE)
595
543
 
596
- ---
544
+ ## Support
597
545
 
598
- **Built with ❤️ by the General Wisdom team**
546
+ - **Issues**: [GitHub Issues](https://github.com/Mission-Sciences/provider-sdk/issues)
547
+ - **Email**: support@generalwisdom.com