postex-auth-sdk-stage 1.0.0 → 1.0.2

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,80 +1,650 @@
1
- # PostEx Auth SDK
1
+ # PostEx Authentication SDK
2
2
 
3
- PostEx Auth SDK for authentication flows: OTP, magic links, passkeys (WebAuthn), and token management. Use it in any frontend project via NPM or CDN.
3
+ ## Developer Documentation
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ The PostEx Authentication SDK provides a comprehensive solution for implementing secure authentication in web applications. It supports multiple authentication methods including:
10
+
11
+ - WebAuthn/Passkeys for passwordless authentication
12
+ - One-Time Password (OTP) verification
13
+ - Magic link authentication
14
+ - DPoP (Demonstration of Proof-of-Possession) for enhanced security
15
+
16
+ ### Key Features
17
+
18
+ - **Multi-factor authentication:** Support for passkeys, OTP, and magic links
19
+ - **DPoP security:** RFC 9449 compliant token binding using ECDSA P-256
20
+ - **Token management:** Automatic token storage, refresh, and cleanup
21
+ - **Trusted device support:** Cookie-based device recognition
22
+ - **Browser compatibility:** Built-in feature detection and fallbacks
23
+
24
+ ---
4
25
 
5
26
  ## Installation
6
27
 
7
- ### NPM
28
+ Install the SDK via npm:
8
29
 
9
30
  ```bash
10
- npm install postex-auth-sdk
31
+ npm install postex-auth-sdk-stage
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Quick Start
37
+
38
+ ### Initialize the SDK
39
+
40
+ ```javascript
41
+ import { AuthSDK } from 'postex-auth-sdk-stage';
42
+
43
+ const auth = new AuthSDK({ apiKey: 'your-api-key' });
44
+ ```
45
+
46
+ ### Basic Authentication Flow
47
+
48
+ ```javascript
49
+ // 1. Check authentication status
50
+ const status = await auth.getStatus('user@example.com');
51
+
52
+ // 2. Initiate authentication
53
+ const result = await auth.initiateAuth('user@example.com');
54
+
55
+ // 3. Handle authentication based on status
56
+ if (result.status === 'webauthn_challenge') {
57
+ // Use passkey authentication
58
+ const authResponse = await auth.authenticateWithPasskey({
59
+ challenge: result.challenge,
60
+ rp: result.rp,
61
+ credentialIds: result.credentialIds
62
+ });
63
+ } else if (result.status === 'otp_sent') {
64
+ // Verify OTP code
65
+ const response = await auth.verifyOTP(otpCode);
66
+ }
67
+ ```
68
+
69
+ ---
70
+
71
+ ## API Reference
72
+
73
+ ### AuthSDK Class
74
+
75
+ The main class for interacting with the authentication service.
76
+
77
+ #### Constructor
78
+
79
+ ```typescript
80
+ new AuthSDK(config: AuthSDKConfig)
81
+ ```
82
+
83
+ **Parameters:**
84
+
85
+ | Parameter | Description |
86
+ |-----------|-------------|
87
+ | `config.apiKey` | (Optional) Your API key for authentication |
88
+
89
+ ---
90
+
91
+ ### Core Authentication Methods
92
+
93
+ #### getStatus()
94
+
95
+ Check if the client has a trusted device session and what authentication method is available.
96
+
97
+ ```typescript
98
+ async getStatus(email: string): Promise<AuthStatusResponse>
11
99
  ```
12
100
 
13
- ### CDN (jsDelivr)
101
+ **Returns:**
102
+
103
+ - `status`: `'no_session'` | `'session_found'` | `'webauthn_ready'`
104
+ - `email`: User's email address
105
+ - `webauthn`: Whether WebAuthn is available
106
+ - `challenge`, `credentialIds`, `rp`: WebAuthn challenge data if available
14
107
 
15
- ```html
16
- <script src="https://cdn.jsdelivr.net/npm/postex-auth-sdk@1/dist/postex-auth-sdk.umd.js"></script>
108
+ ---
109
+
110
+ #### initiateAuth()
111
+
112
+ Start the authentication process. Returns either a WebAuthn challenge or confirms OTP was sent.
113
+
114
+ ```typescript
115
+ async initiateAuth(email: string): Promise<InitiateAuthResponse>
17
116
  ```
18
117
 
19
- ### CDN (unpkg)
118
+ **Returns:**
119
+
120
+ - `status`: `'webauthn_challenge'` | `'otp_sent'`
121
+ - `challenge`, `credentialIds`, `rp`: WebAuthn data if status is `'webauthn_challenge'`
122
+
123
+ ---
124
+
125
+ #### verifyOTP()
126
+
127
+ Verify the OTP code entered by the user. Automatically stores tokens on success.
20
128
 
21
- ```html
22
- <script src="https://unpkg.com/postex-auth-sdk@1/dist/postex-auth-sdk.umd.js"></script>
129
+ ```typescript
130
+ async verifyOTP(otp: string): Promise<OTPVerifyResponse>
23
131
  ```
24
132
 
25
- Use a specific version for production (e.g. `@1.0.0` instead of `@1`).
133
+ **Returns:**
134
+
135
+ - `access_token`: Bearer token for API requests
136
+ - `refresh_token`: Token for refreshing access
137
+ - `id_token`: User identity token
138
+ - `expires_in`: Token expiration time in seconds
139
+ - `verified`, `email`: Verification status and user email
140
+
141
+ ---
142
+
143
+ ### WebAuthn/Passkey Methods
144
+
145
+ #### registerPasskey()
146
+
147
+ Register a new passkey for the user. This enables passwordless authentication.
148
+
149
+ ```typescript
150
+ async registerPasskey(email: string): Promise<PasskeyRegisterResponse>
151
+ ```
152
+
153
+ **Process:**
154
+
155
+ - Initiates passkey registration challenge
156
+ - Triggers browser's passkey creation flow
157
+ - Automatically prevents duplicate registration
158
+ - Returns registration confirmation
159
+
160
+ ---
161
+
162
+ #### authenticateWithPasskey()
163
+
164
+ Authenticate using a registered passkey. Automatically stores tokens on success.
165
+
166
+ ```typescript
167
+ async authenticateWithPasskey({
168
+ challenge: string,
169
+ rp: { name: string; host: string },
170
+ credentialIds: string[]
171
+ }): Promise<AuthResponse>
172
+ ```
26
173
 
27
- ## Usage
174
+ **Parameters:**
28
175
 
29
- ### Via CDN (script tag)
176
+ - `challenge`: WebAuthn challenge from `initiateAuth()`
177
+ - `rp`: Relying party information
178
+ - `credentialIds`: List of allowed credential IDs
30
179
 
31
- After loading the script, the SDK is available on the global `PostexAuthSDK` object:
180
+ ---
32
181
 
33
- ```html
34
- <script src="https://cdn.jsdelivr.net/npm/postex-auth-sdk@1/dist/postex-auth-sdk.umd.js"></script>
35
- <script>
36
- const { AuthSDK, WebAuthn } = PostexAuthSDK;
37
- const sdk = new AuthSDK({ apiKey: "your-api-key" });
182
+ #### getPasskeyStatus()
38
183
 
39
- // Check auth status
40
- const status = await sdk.getStatus("user@example.com");
184
+ Check if a user has registered passkeys.
41
185
 
42
- // Initiate auth (OTP or WebAuthn)
43
- const result = await sdk.initiateAuth("user@example.com");
44
- if (result.status === "webauthn_challenge") {
45
- const authResult = await sdk.authenticateWithPasskey({
46
- challenge: result.challenge,
47
- rp: result.rp,
48
- credentialIds: result.credentialIds || [],
49
- });
50
- } else if (result.status === "otp_sent") {
51
- const verified = await sdk.verifyOTP("123456");
186
+ ```typescript
187
+ async getPasskeyStatus(username: string): Promise<PasskeyStatusResponse>
188
+ ```
189
+
190
+ **Returns:**
191
+
192
+ - `hasCredentials`: Boolean indicating if user has passkeys
193
+ - `credentialId`: The credential ID if available
194
+
195
+ ---
196
+
197
+ #### removePasskey()
198
+
199
+ Remove a user's registered passkey.
200
+
201
+ ```typescript
202
+ async removePasskey(username: string): Promise<void>
203
+ ```
204
+
205
+ ---
206
+
207
+ ### Magic Link Methods
208
+
209
+ #### verifyMagicLink()
210
+
211
+ Verify a magic link token. This sets the authentication session cookie.
212
+
213
+ ```typescript
214
+ async verifyMagicLink(token: string, email: string): Promise<void>
215
+ ```
216
+
217
+ ---
218
+
219
+ #### completeMagicLink()
220
+
221
+ Complete magic link authentication. Must be called after `verifyMagicLink()`. Automatically stores tokens.
222
+
223
+ ```typescript
224
+ async completeMagicLink(): Promise<OTPVerifyResponse>
225
+ ```
226
+
227
+ ---
228
+
229
+ ### Token Management Methods
230
+
231
+ #### refreshToken()
232
+
233
+ Refresh the access token using the server-stored refresh token. Requires a trusted device cookie. Rate limited to 10 requests per minute.
234
+
235
+ ```typescript
236
+ async refreshToken(): Promise<RefreshTokenResponse>
237
+ ```
238
+
239
+ **Returns:**
240
+
241
+ - `access_token`: New access token
242
+ - `id_token`: New ID token
243
+ - `expires_in`: Token expiration time
244
+
245
+ ---
246
+
247
+ #### getAccessToken()
248
+
249
+ Retrieve the stored access token.
250
+
251
+ ```typescript
252
+ async getAccessToken(): Promise<string | null>
253
+ ```
254
+
255
+ ---
256
+
257
+ #### logout()
258
+
259
+ Log out from the current device. Revokes the current token and trusted device, then clears local tokens and DPoP keys.
260
+
261
+ ```typescript
262
+ async logout(): Promise<void>
263
+ ```
264
+
265
+ ---
266
+
267
+ ### DPoP (Proof-of-Possession) Methods
268
+
269
+ DPoP provides enhanced security by binding tokens to cryptographic keys. The SDK implements RFC 9449 using ECDSA P-256.
270
+
271
+ #### getRequestAuthHeaders()
272
+
273
+ Get authentication headers (Authorization + DPoP) for a request. Use this to attach auth to your own HTTP client.
274
+
275
+ ```typescript
276
+ async getRequestAuthHeaders(
277
+ method: string,
278
+ url: string
279
+ ): Promise<Record<string, string>>
280
+ ```
281
+
282
+ **Example:**
283
+
284
+ ```javascript
285
+ const headers = await auth.getRequestAuthHeaders('GET', '/api/user');
286
+ // headers = { Authorization: 'Bearer ...', DPoP: '...' }
287
+ ```
288
+
289
+ ---
290
+
291
+ #### authenticatedFetch()
292
+
293
+ Drop-in replacement for `fetch()` that automatically includes DPoP authentication headers.
294
+
295
+ ```typescript
296
+ async authenticatedFetch(
297
+ input: RequestInfo | URL,
298
+ init?: RequestInit
299
+ ): Promise<Response>
300
+ ```
301
+
302
+ **Example:**
303
+
304
+ ```javascript
305
+ const response = await auth.authenticatedFetch('/api/protected', {
306
+ method: 'POST',
307
+ body: JSON.stringify({ data: 'value' })
308
+ });
309
+ ```
310
+
311
+ ---
312
+
313
+ ## WebAuthn Utility Functions
314
+
315
+ The webauthn module provides utility functions for WebAuthn support detection, data encoding, and passkey email management.
316
+
317
+ ### Browser Support Detection
318
+
319
+ #### isWebAuthnSupported()
320
+
321
+ Check if the browser supports WebAuthn.
322
+
323
+ ```typescript
324
+ function isWebAuthnSupported(): boolean
325
+ ```
326
+
327
+ ---
328
+
329
+ #### isConditionalUISupported()
330
+
331
+ Check if conditional UI (autofill passkeys) is supported.
332
+
333
+ ```typescript
334
+ async function isConditionalUISupported(): Promise<boolean>
335
+ ```
336
+
337
+ ---
338
+
339
+ ### Encoding Functions
340
+
341
+ Functions for converting between ArrayBuffer and base64 formats.
342
+
343
+ ```typescript
344
+ // Convert ArrayBuffer to base64
345
+ function arrayBufferToBase64(buffer: ArrayBuffer): string
346
+
347
+ // Convert base64 to ArrayBuffer
348
+ function base64ToArrayBuffer(base64: string): ArrayBuffer
349
+
350
+ // Convert ArrayBuffer to base64url (URL-safe)
351
+ function arrayBufferToBase64url(buffer: ArrayBuffer): string
352
+
353
+ // Convert string to ArrayBuffer
354
+ function stringToArrayBuffer(str: string): ArrayBuffer
355
+ ```
356
+
357
+ ---
358
+
359
+ ### Passkey Email Management
360
+
361
+ Functions for storing and retrieving the passkey email in IndexedDB.
362
+
363
+ ```typescript
364
+ // Store passkey email
365
+ async function setPasskeyEmail(email: string): Promise<void>
366
+
367
+ // Retrieve passkey email
368
+ async function getPasskeyEmail(): Promise<string | null>
369
+
370
+ // Clear stored passkey email
371
+ async function clearPasskeyEmail(): Promise<void>
372
+ ```
373
+
374
+ ---
375
+
376
+ ### DPoP Utility Functions
377
+
378
+ Low-level DPoP functions. Most developers won't need these as they're handled automatically by the SDK.
379
+
380
+ ```typescript
381
+ // Generate DPoP key pair
382
+ async function generateDPoPKeyPair(): Promise<{
383
+ publicKey: MinimalECPublicKeyJWK;
384
+ thumbprint: string;
385
+ }>
386
+
387
+ // Generate DPoP proof JWT
388
+ async function generateDPoPProof(
389
+ httpMethod: string,
390
+ httpUri: string,
391
+ accessToken?: string
392
+ ): Promise<string | null>
393
+
394
+ // Check if DPoP is enabled
395
+ async function isDPoPEnabled(): Promise<boolean>
396
+
397
+ // Clear DPoP keys
398
+ async function clearDPoPKey(): Promise<void>
399
+ ```
400
+
401
+ ---
402
+
403
+ ## Complete Examples
404
+
405
+ ### Passkey Registration Flow
406
+
407
+ ```javascript
408
+ import { AuthSDK, isWebAuthnSupported } from 'postex-auth-sdk-stage';
409
+
410
+ async function registerUserPasskey(email: string) {
411
+ // Check browser support
412
+ if (!isWebAuthnSupported()) {
413
+ alert('Passkeys not supported in this browser');
414
+ return;
415
+ }
416
+
417
+ const auth = new AuthSDK({ apiKey: 'your-api-key' });
418
+
419
+ try {
420
+ // Register passkey
421
+ const result = await auth.registerPasskey(email);
422
+
423
+ if (result.registered) {
424
+ console.log('Passkey registered successfully');
425
+ }
426
+ } catch (error) {
427
+ console.error('Passkey registration failed:', error);
428
+ }
429
+ }
430
+ ```
431
+
432
+ ---
433
+
434
+ ### OTP Authentication Flow
435
+
436
+ ```javascript
437
+ import { AuthSDK } from 'postex-auth-sdk-stage';
438
+
439
+ async function loginWithOTP(email: string, otpCode: string) {
440
+ const auth = new AuthSDK({ apiKey: 'your-api-key' });
441
+
442
+ try {
443
+ // Initiate authentication
444
+ const initResult = await auth.initiateAuth(email);
445
+
446
+ if (initResult.status !== 'otp_sent') {
447
+ throw new Error('Expected OTP to be sent');
448
+ }
449
+
450
+ // Verify OTP
451
+ const verifyResult = await auth.verifyOTP(otpCode);
452
+
453
+ if (verifyResult.verified) {
454
+ console.log('Authentication successful');
455
+ console.log('Access token:', verifyResult.access_token);
456
+ }
457
+ } catch (error) {
458
+ console.error('Authentication failed:', error);
52
459
  }
53
- </script>
460
+ }
54
461
  ```
55
462
 
56
- ### Via NPM (ESM)
463
+ ---
464
+
465
+ ### Magic Link Authentication Flow
57
466
 
58
467
  ```javascript
59
- import { AuthSDK, WebAuthn, AuthSDKFetchError } from "postex-auth-sdk";
468
+ import { AuthSDK } from 'postex-auth-sdk-stage';
469
+
470
+ async function handleMagicLink(token: string, email: string) {
471
+ const auth = new AuthSDK({ apiKey: 'your-api-key' });
60
472
 
61
- const sdk = new AuthSDK({ apiKey: "your-api-key" });
473
+ try {
474
+ // Step 1: Verify the magic link token
475
+ await auth.verifyMagicLink(token, email);
62
476
 
63
- // Use SDK methods
64
- const status = await sdk.getStatus("user@example.com");
477
+ // Step 2: Complete authentication
478
+ const result = await auth.completeMagicLink();
479
+
480
+ console.log('Magic link authentication successful');
481
+ console.log('Access token:', result.access_token);
482
+ } catch (error) {
483
+ console.error('Magic link authentication failed:', error);
484
+ }
485
+ }
65
486
  ```
66
487
 
67
- ### WebAuthn utilities
488
+ ---
489
+
490
+ ### Making Authenticated API Requests
68
491
 
69
492
  ```javascript
70
- import { WebAuthn } from "postex-auth-sdk";
493
+ import { AuthSDK } from 'postex-auth-sdk-stage';
494
+
495
+ const auth = new AuthSDK({ apiKey: 'your-api-key' });
496
+
497
+ // Option 1: Use authenticatedFetch (recommended)
498
+ const response1 = await auth.authenticatedFetch('/api/user/profile');
499
+ const userData = await response1.json();
500
+
501
+ // Option 2: Get headers for your own HTTP client
502
+ const headers = await auth.getRequestAuthHeaders(
503
+ 'POST',
504
+ 'https://api.example.com/data'
505
+ );
506
+
507
+ const response2 = await fetch('https://api.example.com/data', {
508
+ method: 'POST',
509
+ headers: {
510
+ ...headers,
511
+ 'Content-Type': 'application/json'
512
+ },
513
+ body: JSON.stringify({ key: 'value' })
514
+ });
515
+ ```
516
+
517
+ ---
518
+
519
+ ## Error Handling
71
520
 
72
- if (WebAuthn.isWebAuthnSupported()) {
73
- const conditionalUI = await WebAuthn.isConditionalUISupported();
74
- // ...
521
+ The SDK throws `AuthSDKFetchError` for HTTP errors. This error includes the status code and response data.
522
+
523
+ ```javascript
524
+ import { AuthSDK, AuthSDKFetchError } from 'postex-auth-sdk-stage';
525
+
526
+ try {
527
+ await auth.verifyOTP(otpCode);
528
+ } catch (error) {
529
+ if (error instanceof AuthSDKFetchError) {
530
+ console.error('Status:', error.response.status);
531
+ console.error('Data:', error.response.data);
532
+
533
+ if (error.response.status === 401) {
534
+ // Token expired - attempt refresh
535
+ try {
536
+ await auth.refreshToken();
537
+ } catch (refreshError) {
538
+ // Refresh failed - redirect to login
539
+ window.location.href = '/login';
540
+ }
541
+ }
542
+ }
75
543
  }
76
544
  ```
77
545
 
546
+ ---
547
+
548
+ ## Security Considerations
549
+
550
+ ### DPoP Token Binding
551
+
552
+ The SDK automatically implements DPoP (RFC 9449) to bind tokens to cryptographic keys. This prevents token theft and replay attacks. DPoP keys are:
553
+
554
+ - Generated using ECDSA P-256 (ES256)
555
+ - Stored securely in IndexedDB
556
+ - Non-extractable (private key cannot be exported)
557
+ - Automatically included in all authenticated requests
558
+
559
+ ---
560
+
561
+ ### Token Storage
562
+
563
+ Tokens are stored in localStorage with the following keys:
564
+
565
+ - `postex-auth-token`: Access token
566
+ - `auth_sdk_refresh_token`: Refresh token
567
+ - `auth_sdk_id_token`: ID token
568
+
569
+ ---
570
+
571
+ ### Trusted Device Cookies
572
+
573
+ The backend sets a secure, HTTP-only cookie (`td`) to identify trusted devices. This enables:
574
+
575
+ - Token refresh without re-authentication
576
+ - Session persistence across page reloads
577
+ - Device-specific security policies
578
+
579
+ ---
580
+
581
+ ### Best Practices
582
+
583
+ - **Always use HTTPS:** The SDK requires secure contexts for WebAuthn and cryptographic operations
584
+ - **Handle token expiration:** Implement automatic token refresh before expiration
585
+ - **Clear tokens on logout:** Always call `auth.logout()` when users sign out
586
+ - **Validate on backend:** Never trust client-side authentication alone
587
+ - **Monitor 401 errors:** The SDK automatically clears tokens on 401 responses
588
+
589
+ ---
590
+
591
+ ## Browser Compatibility
592
+
593
+ ### WebAuthn Support
594
+
595
+ WebAuthn/Passkeys are supported in:
596
+
597
+ - Chrome/Edge 67+
598
+ - Firefox 60+
599
+ - Safari 13+
600
+ - Opera 54+
601
+
602
+ ### Fallback Authentication
603
+
604
+ For browsers without WebAuthn support, the SDK automatically falls back to OTP authentication. Always check browser support before attempting passkey operations:
605
+
606
+ ```javascript
607
+ import { isWebAuthnSupported } from 'postex-auth-sdk-stage';
608
+
609
+ if (isWebAuthnSupported()) {
610
+ // Offer passkey registration/authentication
611
+ } else {
612
+ // Use OTP or magic link
613
+ }
614
+ ```
615
+
616
+ ---
617
+
618
+ ## TypeScript Support
619
+
620
+ The SDK is written in TypeScript and includes full type definitions. All interfaces are exported for your convenience:
621
+
622
+ ```typescript
623
+ import {
624
+ AuthSDK,
625
+ AuthSDKConfig,
626
+ AuthStatusResponse,
627
+ InitiateAuthResponse,
628
+ OTPVerifyResponse,
629
+ AuthResponse,
630
+ PasskeyRegisterResponse,
631
+ PasskeyStatusResponse,
632
+ RefreshTokenResponse,
633
+ AuthSDKFetchError
634
+ } from 'postex-auth-sdk-stage';
635
+ ```
636
+
637
+ ---
638
+
639
+ ## Support and Resources
640
+
641
+ - **API Documentation:** https://auth-stage.postexglobal.com/docs
642
+ - **GitHub Repository:** (Add your repository link)
643
+ - **Issue Tracker:** (Add your issue tracker link)
644
+ - **Email Support:** (Add your support email)
645
+
646
+ ---
647
+
78
648
  ## API overview
79
649
 
80
650
  - **AuthSDK** – Main client: `getStatus`, `initiateAuth`, `verifyOTP`, `verifyMagicLink`, `completeMagicLink`, `authenticateWithPasskey`, `registerPasskey`, `getPasskeyStatus`, `removePasskey`, `getRequestAuthHeaders`, `authenticatedFetch`, `refreshToken`, `logout`, `getAccessToken`, `storeTokens`, `clearTokens`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postex-auth-sdk-stage",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "PostEx Auth SDK for authentication flows: OTP, magic links, passkeys, and token management",
5
5
  "main": "./dist/postex-auth-sdk-stage.umd.js",
6
6
  "module": "./dist/postex-auth-sdk-stage.es.js",