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 +612 -42
- package/package.json +1 -1
- package/dist/auth.d.ts +0 -189
- package/dist/main.d.ts +0 -2
- package/dist/postex-auth-sdk-stage.es.js +0 -632
- package/dist/postex-auth-sdk-stage.es.js.map +0 -1
- package/dist/postex-auth-sdk-stage.iife.js +0 -2
- package/dist/postex-auth-sdk-stage.iife.js.map +0 -1
- package/dist/postex-auth-sdk-stage.umd.js +0 -2
- package/dist/postex-auth-sdk-stage.umd.js.map +0 -1
- package/dist/vite.svg +0 -1
- package/dist/webauthn.d.ts +0 -75
package/README.md
CHANGED
|
@@ -1,80 +1,650 @@
|
|
|
1
|
-
# PostEx
|
|
1
|
+
# PostEx Authentication SDK
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
22
|
-
<
|
|
129
|
+
```typescript
|
|
130
|
+
async verifyOTP(otp: string): Promise<OTPVerifyResponse>
|
|
23
131
|
```
|
|
24
132
|
|
|
25
|
-
|
|
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
|
-
|
|
174
|
+
**Parameters:**
|
|
28
175
|
|
|
29
|
-
|
|
176
|
+
- `challenge`: WebAuthn challenge from `initiateAuth()`
|
|
177
|
+
- `rp`: Relying party information
|
|
178
|
+
- `credentialIds`: List of allowed credential IDs
|
|
30
179
|
|
|
31
|
-
|
|
180
|
+
---
|
|
32
181
|
|
|
33
|
-
|
|
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
|
-
|
|
40
|
-
const status = await sdk.getStatus("user@example.com");
|
|
184
|
+
Check if a user has registered passkeys.
|
|
41
185
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
460
|
+
}
|
|
54
461
|
```
|
|
55
462
|
|
|
56
|
-
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### Magic Link Authentication Flow
|
|
57
466
|
|
|
58
467
|
```javascript
|
|
59
|
-
import { AuthSDK
|
|
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
|
-
|
|
473
|
+
try {
|
|
474
|
+
// Step 1: Verify the magic link token
|
|
475
|
+
await auth.verifyMagicLink(token, email);
|
|
62
476
|
|
|
63
|
-
//
|
|
64
|
-
const
|
|
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
|
-
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
### Making Authenticated API Requests
|
|
68
491
|
|
|
69
492
|
```javascript
|
|
70
|
-
import {
|
|
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
|
-
|
|
73
|
-
|
|
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.
|
|
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",
|