@pelican-identity/auth-core 1.2.0 → 1.2.3
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 +570 -0
- package/dist/engine/engine.d.ts.map +1 -0
- package/dist/engine/engine.js +238 -0
- package/dist/engine/engine.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
# Pelican Auth SDK
|
|
2
|
+
|
|
3
|
+
A modern, event-driven JavaScript/TypeScript SDK for integrating Pelican Vault authentication into your web applications.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎯 **Event-Driven Architecture** - Subscribe to authentication lifecycle events
|
|
8
|
+
- 🔄 **Continuous Mode** - Automatic session restart for repeated authentication
|
|
9
|
+
- 📱 **Multi-Platform** - Supports both QR code (desktop) and deep linking (mobile)
|
|
10
|
+
- 🔐 **Secure** - End-to-end encrypted authentication using TweetNaCl
|
|
11
|
+
- 🔌 **WebSocket Transport** - Real-time communication with Pelican Vault
|
|
12
|
+
- 🎨 **Framework Agnostic** - Works with React, Vue, Svelte, or vanilla JS
|
|
13
|
+
- 📦 **Type-Safe** - Full TypeScript support with comprehensive type definitions
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm @pelican-identity/sdk
|
|
19
|
+
# or
|
|
20
|
+
yarn add @pelican-identity/sdk
|
|
21
|
+
# or
|
|
22
|
+
pnpm add @pelican-identity/sdk
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### Basic Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { PelicanAuth } from "@pelican-identity/sdk";
|
|
31
|
+
|
|
32
|
+
// Initialize the SDK
|
|
33
|
+
const auth = new PelicanAuth({
|
|
34
|
+
publicKey: "your-public-key",
|
|
35
|
+
projectId: "your-project-id",
|
|
36
|
+
authType: "login", // 'login' | 'signup' | 'id-verification'
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Listen for authentication success
|
|
40
|
+
auth.on("success", (identity) => {
|
|
41
|
+
console.log("User authenticated:", identity);
|
|
42
|
+
// Handle successful authentication
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Listen for errors
|
|
46
|
+
auth.on("error", (error) => {
|
|
47
|
+
console.error("Authentication failed:", error);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Listen for QR code (desktop) or deep link (mobile)
|
|
51
|
+
auth.on("qr", (qrDataUrl) => {
|
|
52
|
+
// Display QR code image
|
|
53
|
+
document.getElementById("qr").src = qrDataUrl;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
auth.on("deeplink", (url) => {
|
|
57
|
+
// Redirect to Pelican Vault app
|
|
58
|
+
window.location.href = url;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Start authentication
|
|
62
|
+
auth.start();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## API Reference
|
|
66
|
+
|
|
67
|
+
### Constructor
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
new PelicanAuth(config: PelicanAuthConfig)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Configuration Options
|
|
74
|
+
|
|
75
|
+
| Option | Type | Required | Default | Description |
|
|
76
|
+
| ---------------- | ------------------------------------------ | -------- | ------- | ------------------------------------ |
|
|
77
|
+
| `publicKey` | `string` | ✅ Yes | - | Your Pelican Vault public key |
|
|
78
|
+
| `projectId` | `string` | ✅ Yes | - | Your project identifier |
|
|
79
|
+
| `authType` | `'login' \| 'signup' \| 'id-verification'` | ✅ Yes | - | Type of authentication flow |
|
|
80
|
+
| `continuousMode` | `boolean` | ❌ No | `false` | Auto-restart after successful auth |
|
|
81
|
+
| `forceQRCode` | `boolean` | ❌ No | `false` | Force QR code even on mobile devices |
|
|
82
|
+
|
|
83
|
+
### Methods
|
|
84
|
+
|
|
85
|
+
#### `start(): Promise<void>`
|
|
86
|
+
|
|
87
|
+
Initiates the authentication flow.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
await auth.start();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### `stop(): void`
|
|
94
|
+
|
|
95
|
+
Stops the authentication flow and cleans up resources.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
auth.stop();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `on<K>(event: K, callback: Listener<K>): () => void`
|
|
102
|
+
|
|
103
|
+
Subscribe to authentication events. Returns an unsubscribe function.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const unsubscribe = auth.on("success", (identity) => {
|
|
107
|
+
console.log(identity);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Later: unsubscribe
|
|
111
|
+
unsubscribe();
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
#### `destroy(): void`
|
|
115
|
+
|
|
116
|
+
Completely destroys the SDK instance, removing all listeners and cleanup.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
auth.destroy();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Events
|
|
123
|
+
|
|
124
|
+
The SDK emits the following events:
|
|
125
|
+
|
|
126
|
+
#### `state`
|
|
127
|
+
|
|
128
|
+
Emitted when the authentication state changes.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
auth.on("state", (state) => {
|
|
132
|
+
console.log("Current state:", state);
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**States:**
|
|
137
|
+
|
|
138
|
+
- `idle` - Initial state
|
|
139
|
+
- `initializing` - Fetching relay server
|
|
140
|
+
- `awaiting-pair` - Waiting for mobile device to scan
|
|
141
|
+
- `paired` - Device connected
|
|
142
|
+
- `authenticated` - User authenticated
|
|
143
|
+
- `confirmed` - Authentication confirmed
|
|
144
|
+
- `terminated` - Session ended
|
|
145
|
+
- `error` - Error occurred
|
|
146
|
+
|
|
147
|
+
#### `success`
|
|
148
|
+
|
|
149
|
+
Emitted when authentication succeeds.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
auth.on("success", (identity: IdentityResult) => {
|
|
153
|
+
console.log("User ID:", identity.user_id);
|
|
154
|
+
console.log("Email:", identity.user_data.email.value);
|
|
155
|
+
console.log("Phone:", identity.user_data.phone.number);
|
|
156
|
+
console.log("First Name:", identity.user_data.first_name);
|
|
157
|
+
|
|
158
|
+
console.log("ID Verification:", identity.id_verification);
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**IdentityResult:**
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface IdentityResult {
|
|
166
|
+
user_id: string;
|
|
167
|
+
user_data: IUserData;
|
|
168
|
+
id_verification: IKycData;
|
|
169
|
+
id_downloadurls?: { front_of_card?: string; back_of_card?: string };
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `error`
|
|
174
|
+
|
|
175
|
+
Emitted when an error occurs.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
auth.on("error", (error: Error) => {
|
|
179
|
+
console.error("Error:", error.message);
|
|
180
|
+
});
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### `qr`
|
|
184
|
+
|
|
185
|
+
Emitted on desktop with QR code data URL.
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
auth.on("qr", (dataUrl: string) => {
|
|
189
|
+
// Display in an <img> tag
|
|
190
|
+
document.getElementById("qr").src = dataUrl;
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### `deeplink`
|
|
195
|
+
|
|
196
|
+
Emitted on mobile with deep link URL.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
auth.on("deeplink", (url: string) => {
|
|
200
|
+
// Open Pelican Vault app
|
|
201
|
+
window.location.href = url;
|
|
202
|
+
// Or use as href in a button
|
|
203
|
+
document.getElementById("login-btn").href = url;
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Usage Examples
|
|
208
|
+
|
|
209
|
+
### React Hook
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { useEffect, useState } from "react";
|
|
213
|
+
import { PelicanAuth } from "@pelican-identity/sdk";
|
|
214
|
+
|
|
215
|
+
function usePelicanAuth(config) {
|
|
216
|
+
const [state, setState] = useState("idle");
|
|
217
|
+
const [qrCode, setQrCode] = useState(null);
|
|
218
|
+
const [deepLink, setDeepLink] = useState(null);
|
|
219
|
+
const [error, setError] = useState(null);
|
|
220
|
+
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
const auth = new PelicanAuth(config);
|
|
223
|
+
|
|
224
|
+
auth.on("state", setState);
|
|
225
|
+
auth.on("qr", setQrCode);
|
|
226
|
+
auth.on("deeplink", setDeepLink);
|
|
227
|
+
auth.on("error", setError);
|
|
228
|
+
|
|
229
|
+
auth.on("success", (identity) => {
|
|
230
|
+
console.log("Authenticated:", identity);
|
|
231
|
+
config.onSuccess?.(identity);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return () => auth.destroy();
|
|
235
|
+
}, []);
|
|
236
|
+
|
|
237
|
+
return { state, qrCode, deepLink, error };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Usage
|
|
241
|
+
function LoginButton() {
|
|
242
|
+
const { state, qrCode, deepLink } = usePelicanAuth({
|
|
243
|
+
publicKey: "your-key",
|
|
244
|
+
projectId: "your-project",
|
|
245
|
+
authType: "login",
|
|
246
|
+
onSuccess: (identity) => {
|
|
247
|
+
// Handle login
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
if (qrCode) {
|
|
252
|
+
return <img src={qrCode} alt="Scan to login" />;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (deepLink) {
|
|
256
|
+
return <a href={deepLink}>Open Pelican App</a>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return <button onClick={() => auth.start()}>Login</button>;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Vue Composition API
|
|
264
|
+
|
|
265
|
+
```vue
|
|
266
|
+
<script setup>
|
|
267
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
268
|
+
import { PelicanAuth } from "@pelican-identity/pelican-sdk";
|
|
269
|
+
|
|
270
|
+
const props = defineProps(["publicKey", "projectId", "authType"]);
|
|
271
|
+
const emit = defineEmits(["success", "error"]);
|
|
272
|
+
|
|
273
|
+
const state = ref("idle");
|
|
274
|
+
const qrCode = ref(null);
|
|
275
|
+
const deepLink = ref(null);
|
|
276
|
+
|
|
277
|
+
let auth;
|
|
278
|
+
|
|
279
|
+
onMounted(() => {
|
|
280
|
+
auth = new PelicanAuth({
|
|
281
|
+
publicKey: props.publicKey,
|
|
282
|
+
projectId: props.projectId,
|
|
283
|
+
authType: props.authType,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
auth.on("state", (s) => (state.value = s));
|
|
287
|
+
auth.on("qr", (qr) => (qrCode.value = qr));
|
|
288
|
+
auth.on("deeplink", (link) => (deepLink.value = link));
|
|
289
|
+
auth.on("success", (identity) => emit("success", identity));
|
|
290
|
+
auth.on("error", (err) => emit("error", err));
|
|
291
|
+
|
|
292
|
+
auth.start();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
onUnmounted(() => {
|
|
296
|
+
auth?.destroy();
|
|
297
|
+
});
|
|
298
|
+
</script>
|
|
299
|
+
|
|
300
|
+
<template>
|
|
301
|
+
<div>
|
|
302
|
+
<img v-if="qrCode" :src="qrCode" alt="QR Code" />
|
|
303
|
+
<a v-else-if="deepLink" :href="deepLink">Open App</a>
|
|
304
|
+
<p v-else>{{ state }}</p>
|
|
305
|
+
</div>
|
|
306
|
+
</template>
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Vanilla JavaScript
|
|
310
|
+
|
|
311
|
+
```javascript
|
|
312
|
+
import { PelicanAuth } from "@pelican-identity/sdk";
|
|
313
|
+
|
|
314
|
+
// Create container
|
|
315
|
+
const container = document.getElementById("auth-container");
|
|
316
|
+
|
|
317
|
+
// Initialize
|
|
318
|
+
const auth = new PelicanAuth({
|
|
319
|
+
publicKey: "your-key",
|
|
320
|
+
projectId: "your-project",
|
|
321
|
+
authType: "login",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Handle QR code
|
|
325
|
+
auth.on("qr", (dataUrl) => {
|
|
326
|
+
container.innerHTML = `
|
|
327
|
+
<img src="${dataUrl}" alt="Scan to login" />
|
|
328
|
+
<p>Scan with Pelican Vault app</p>
|
|
329
|
+
`;
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Handle deep link
|
|
333
|
+
auth.on("deeplink", (url) => {
|
|
334
|
+
container.innerHTML = `
|
|
335
|
+
<a href="${url}" class="btn">Open Pelican App</a>
|
|
336
|
+
`;
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// Handle success
|
|
340
|
+
auth.on("success", (identity) => {
|
|
341
|
+
console.log("Logged in as:", identity.user_id);
|
|
342
|
+
// Redirect or update UI
|
|
343
|
+
window.location.href = "/dashboard";
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Handle errors
|
|
347
|
+
auth.on("error", (error) => {
|
|
348
|
+
container.innerHTML = `
|
|
349
|
+
<p class="error">${error.message}</p>
|
|
350
|
+
<button onclick="auth.start()">Retry</button>
|
|
351
|
+
`;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Start authentication
|
|
355
|
+
auth.start();
|
|
356
|
+
|
|
357
|
+
// Cleanup on page unload
|
|
358
|
+
window.addEventListener("beforeunload", () => {
|
|
359
|
+
auth.destroy();
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Advanced Features
|
|
364
|
+
|
|
365
|
+
### Continuous Mode
|
|
366
|
+
|
|
367
|
+
Automatically restart authentication sessions after completion. Useful for kiosks or multi-user scenarios.
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
const auth = new PelicanAuth({
|
|
371
|
+
publicKey: "your-key",
|
|
372
|
+
projectId: "your-project",
|
|
373
|
+
authType: "login",
|
|
374
|
+
continuousMode: true, // Enable continuous mode
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
auth.on("success", (identity) => {
|
|
378
|
+
// Process user
|
|
379
|
+
console.log("User authenticated:", identity);
|
|
380
|
+
// Session will automatically restart after ~300ms
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
auth.start();
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Force QR Code on Mobile
|
|
387
|
+
|
|
388
|
+
By default, mobile devices use deep links. Force QR code display instead:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
const auth = new PelicanAuth({
|
|
392
|
+
publicKey: "your-key",
|
|
393
|
+
projectId: "your-project",
|
|
394
|
+
authType: "login",
|
|
395
|
+
forceQRCode: true, // Show QR even on mobile
|
|
396
|
+
});
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
### Session Recovery
|
|
400
|
+
|
|
401
|
+
The SDK automatically handles session recovery when users return from the Pelican Vault app:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Session is automatically cached when deep link is opened
|
|
405
|
+
auth.on("deeplink", (url) => {
|
|
406
|
+
// User clicks and goes to Pelican app
|
|
407
|
+
window.location.href = url;
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// When user returns (visibility change), session is recovered automatically
|
|
411
|
+
// Success event will fire if authentication completed
|
|
412
|
+
auth.on("success", (identity) => {
|
|
413
|
+
console.log("Recovered session:", identity);
|
|
414
|
+
});
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### State Machine Tracking
|
|
418
|
+
|
|
419
|
+
Track the complete authentication lifecycle:
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
auth.on("state", (state) => {
|
|
423
|
+
switch (state) {
|
|
424
|
+
case "idle":
|
|
425
|
+
console.log("Ready to start");
|
|
426
|
+
break;
|
|
427
|
+
case "initializing":
|
|
428
|
+
console.log("Connecting to server...");
|
|
429
|
+
break;
|
|
430
|
+
case "awaiting-pair":
|
|
431
|
+
console.log("Waiting for device...");
|
|
432
|
+
break;
|
|
433
|
+
case "paired":
|
|
434
|
+
console.log("Device connected!");
|
|
435
|
+
break;
|
|
436
|
+
case "authenticated":
|
|
437
|
+
console.log("User authenticated!");
|
|
438
|
+
break;
|
|
439
|
+
case "confirmed":
|
|
440
|
+
console.log("Session confirmed");
|
|
441
|
+
break;
|
|
442
|
+
case "error":
|
|
443
|
+
console.log("Error occurred");
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
## Error Handling
|
|
450
|
+
|
|
451
|
+
The SDK provides detailed error information:
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
auth.on("error", (error) => {
|
|
455
|
+
console.error("Error type:", error.message);
|
|
456
|
+
|
|
457
|
+
// Common errors:
|
|
458
|
+
// - "Failed to fetch relay URL" - Server connection issue
|
|
459
|
+
// - "WebSocket connection failed" - Network issue
|
|
460
|
+
// - "Invalid authentication payload" - Data corruption
|
|
461
|
+
// - "Failed to decrypt authentication data" - Crypto error
|
|
462
|
+
// - "Authenticating device terminated the connection" - User cancelled
|
|
463
|
+
// - "Session recovery failed" - Invalid cached session
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Security Considerations
|
|
468
|
+
|
|
469
|
+
1. **Public Key and Project ID**: It is recommended to use environment variables or fetch from a secure server for production.
|
|
470
|
+
2. **HTTPS Required**: Always use HTTPS in production
|
|
471
|
+
3. **Session Expiry**: Sessions expire after 5 minutes by default
|
|
472
|
+
4. **End-to-End Encryption**: All data is encrypted using TweetNaCl (NaCl crypto)
|
|
473
|
+
5. **No Password Storage**: Authentication happens entirely through the Pelican Vault app
|
|
474
|
+
|
|
475
|
+
## Browser Support
|
|
476
|
+
|
|
477
|
+
- Chrome/Edge 90+
|
|
478
|
+
- Firefox 88+
|
|
479
|
+
- Safari 14+
|
|
480
|
+
- Opera 76+
|
|
481
|
+
|
|
482
|
+
**Required APIs:**
|
|
483
|
+
|
|
484
|
+
- WebSocket
|
|
485
|
+
- Crypto API (`crypto.randomUUID()`)
|
|
486
|
+
- sessionStorage
|
|
487
|
+
- Visibility API
|
|
488
|
+
|
|
489
|
+
## Troubleshooting
|
|
490
|
+
|
|
491
|
+
### QR Code Not Displaying
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
auth.on("qr", (dataUrl) => {
|
|
495
|
+
if (!dataUrl) {
|
|
496
|
+
console.error("QR code generation failed");
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### WebSocket Connection Issues
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
// Check network connectivity
|
|
505
|
+
auth.on("error", (error) => {
|
|
506
|
+
if (error.message.includes("WebSocket")) {
|
|
507
|
+
console.log("Check network connection");
|
|
508
|
+
// Retry logic
|
|
509
|
+
setTimeout(() => auth.start(), 5000);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Session Recovery Not Working
|
|
515
|
+
|
|
516
|
+
Ensure visibility change detection is working:
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
document.addEventListener("visibilitychange", () => {
|
|
520
|
+
console.log("Visibility changed:", document.visibilityState);
|
|
521
|
+
});
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## TypeScript Support
|
|
525
|
+
|
|
526
|
+
Full TypeScript definitions included:
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
import type {
|
|
530
|
+
IEmail,
|
|
531
|
+
IKycData,
|
|
532
|
+
IPhone,
|
|
533
|
+
IUserData,
|
|
534
|
+
IdentityResult,
|
|
535
|
+
IdCardTypes,
|
|
536
|
+
} from "@pelican-identity/sdk";
|
|
537
|
+
import {
|
|
538
|
+
AuthType,
|
|
539
|
+
ISocketMessage,
|
|
540
|
+
PelicanAuthConfig,
|
|
541
|
+
PelicanAuthEventMap,
|
|
542
|
+
PelicanAuthState,
|
|
543
|
+
} from "@pelican-identity/sdk";
|
|
544
|
+
|
|
545
|
+
const config: PelicanAuthConfig = {
|
|
546
|
+
publicKey: "your-key",
|
|
547
|
+
projectId: "your-project",
|
|
548
|
+
authType: "login",
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
const auth = new PelicanAuth(config);
|
|
552
|
+
|
|
553
|
+
auth.on("success", (identity: IdentityResult) => {
|
|
554
|
+
const userId: string = identity.user_id;
|
|
555
|
+
});
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
## License
|
|
559
|
+
|
|
560
|
+
MIT
|
|
561
|
+
|
|
562
|
+
## Support
|
|
563
|
+
|
|
564
|
+
- Documentation: https://docs.pelicanidentity.com
|
|
565
|
+
- Issues: https://github.com/pelican-identity/sdk/issues
|
|
566
|
+
- Email: support@pelicanidentity.com
|
|
567
|
+
|
|
568
|
+
## Changelog
|
|
569
|
+
|
|
570
|
+
See [CHANGELOG.md](CHANGELOG.md) for release history.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine/engine.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EAGpB,MAAM,gBAAgB,CAAC;AAYxB,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,CAAC;AAMxC,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAC9C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAsB;IAEnD,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,UAAU,CAAuB;IAEzC,OAAO,CAAC,iBAAiB,CAAC,CAAa;IAEvC,OAAO,CAAC,SAAS,CAEV;IAEP,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;gBAEzC,MAAM,EAAE,iBAAiB;IAsBrC,EAAE,CAAC,CAAC,SAAS,MAAM,mBAAmB,EACpC,KAAK,EAAE,CAAC,EACR,EAAE,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;IAWhC,KAAK;IAqCX,IAAI;YAOU,aAAa;YAiBb,cAAc;IA+B5B,OAAO,CAAC,aAAa;IAqCrB,OAAO,CAAC,iBAAiB;YAwCX,cAAc;IA4B5B,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,wBAAwB;IAQhC,OAAO,CAAC,SAAS;IAyBjB,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,YAAY;IAMpB,OAAO;IAMP,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,IAAI;CAIb"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import CryptoService from "../utilities/crypto";
|
|
2
|
+
import QRCode from "qrcode";
|
|
3
|
+
import { storeAuthSession, getAuthSession, clearAuthSession, } from "../utilities/storage";
|
|
4
|
+
import { StateMachine } from "../utilities/stateMachine";
|
|
5
|
+
import { Transport } from "../utilities/transport";
|
|
6
|
+
import { BASEURL } from "../constants";
|
|
7
|
+
export class PelicanAuthentication {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.crypto = new CryptoService();
|
|
10
|
+
this.stateMachine = new StateMachine();
|
|
11
|
+
this.sessionId = "";
|
|
12
|
+
this.sessionKey = null;
|
|
13
|
+
this.listeners = {};
|
|
14
|
+
if (!config.publicKey)
|
|
15
|
+
throw new Error("Missing publicKey");
|
|
16
|
+
if (!config.projectId)
|
|
17
|
+
throw new Error("Missing projectId");
|
|
18
|
+
if (!config.authType)
|
|
19
|
+
throw new Error("Missing authType");
|
|
20
|
+
this.config = {
|
|
21
|
+
continuousMode: false,
|
|
22
|
+
forceQRCode: false,
|
|
23
|
+
...config,
|
|
24
|
+
};
|
|
25
|
+
this.stateMachine.subscribe((s) => this.emit("state", s));
|
|
26
|
+
this.attachVisibilityRecovery();
|
|
27
|
+
}
|
|
28
|
+
on(event, cb) {
|
|
29
|
+
var _a;
|
|
30
|
+
(_a = this.listeners)[event] ?? (_a[event] = new Set());
|
|
31
|
+
this.listeners[event].add(cb);
|
|
32
|
+
return () => this.listeners[event].delete(cb);
|
|
33
|
+
}
|
|
34
|
+
async start() {
|
|
35
|
+
if (this.stateMachine.current !== "idle")
|
|
36
|
+
return;
|
|
37
|
+
this.resetSession();
|
|
38
|
+
this.stateMachine.transition("initializing");
|
|
39
|
+
try {
|
|
40
|
+
const relay = await this.fetchRelayUrl();
|
|
41
|
+
this.sessionKey = this.crypto.generateSymmetricKey();
|
|
42
|
+
this.sessionId = crypto.randomUUID() + crypto.randomUUID();
|
|
43
|
+
this.transport = new Transport({
|
|
44
|
+
onOpen: () => {
|
|
45
|
+
this.transport.send({
|
|
46
|
+
type: "register",
|
|
47
|
+
sessionID: this.sessionId,
|
|
48
|
+
...this.config,
|
|
49
|
+
});
|
|
50
|
+
this.stateMachine.transition("awaiting-pair");
|
|
51
|
+
},
|
|
52
|
+
onMessage: (msg) => this.handleMessage(msg),
|
|
53
|
+
onError: () => this.fail(new Error("WebSocket connection failed")),
|
|
54
|
+
});
|
|
55
|
+
this.transport.connect(relay);
|
|
56
|
+
await this.emitEntryPoint();
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
this.fail(err instanceof Error ? err : new Error("Start failed"));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
stop() {
|
|
63
|
+
this.terminate(false);
|
|
64
|
+
}
|
|
65
|
+
async fetchRelayUrl() {
|
|
66
|
+
const { publicKey, projectId, authType } = this.config;
|
|
67
|
+
const res = await fetch(`${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`);
|
|
68
|
+
if (!res.ok) {
|
|
69
|
+
throw new Error("Failed to fetch relay URL");
|
|
70
|
+
}
|
|
71
|
+
const json = await res.json();
|
|
72
|
+
return json.relay_url;
|
|
73
|
+
}
|
|
74
|
+
async emitEntryPoint() {
|
|
75
|
+
const payload = {
|
|
76
|
+
sessionID: this.sessionId,
|
|
77
|
+
sessionKey: this.sessionKey,
|
|
78
|
+
publicKey: this.config.publicKey,
|
|
79
|
+
authType: this.config.authType,
|
|
80
|
+
projectId: this.config.projectId,
|
|
81
|
+
url: window.location.href,
|
|
82
|
+
};
|
|
83
|
+
const shouldUseQR = this.config.forceQRCode ||
|
|
84
|
+
!/Android|iPhone|iPad/i.test(navigator.userAgent);
|
|
85
|
+
if (!shouldUseQR && this.sessionKey) {
|
|
86
|
+
storeAuthSession(this.sessionId, this.sessionKey, 5 * 60000);
|
|
87
|
+
this.emit("deeplink", `pelicanvault://auth/deep-link?data=${encodeURIComponent(JSON.stringify(payload))}`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const qr = await QRCode.toDataURL(JSON.stringify(payload));
|
|
91
|
+
this.emit("qr", qr);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
handleMessage(msg) {
|
|
95
|
+
console.log(msg);
|
|
96
|
+
switch (msg.type) {
|
|
97
|
+
case "paired":
|
|
98
|
+
console.log("Paired");
|
|
99
|
+
this.stateMachine.transition("paired");
|
|
100
|
+
this.transport.send({
|
|
101
|
+
type: "authenticate",
|
|
102
|
+
sessionID: this.sessionId,
|
|
103
|
+
...this.config,
|
|
104
|
+
url: window.location.href,
|
|
105
|
+
});
|
|
106
|
+
return;
|
|
107
|
+
case "phone-auth-success":
|
|
108
|
+
this.handleAuthSuccess(msg);
|
|
109
|
+
return;
|
|
110
|
+
case "phone-terminated":
|
|
111
|
+
this.fail(new Error("Authenticating device terminated the connection"));
|
|
112
|
+
this.restartIfContinuous();
|
|
113
|
+
return;
|
|
114
|
+
case "confirmed":
|
|
115
|
+
this.terminate(true);
|
|
116
|
+
this.restartIfContinuous();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
handleAuthSuccess(msg) {
|
|
121
|
+
if (!this.sessionKey || !msg.cipher || !msg.nonce) {
|
|
122
|
+
this.fail(new Error("Invalid authentication payload"));
|
|
123
|
+
this.restartIfContinuous();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
const decrypted = this.crypto.decryptSymmetric({
|
|
128
|
+
encrypted: { cipher: msg.cipher, nonce: msg.nonce },
|
|
129
|
+
keyString: this.sessionKey,
|
|
130
|
+
});
|
|
131
|
+
if (!decrypted) {
|
|
132
|
+
this.fail(new Error("Invalid authentication data"));
|
|
133
|
+
this.restartIfContinuous();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const result = JSON.parse(decrypted);
|
|
137
|
+
this.emit("success", result);
|
|
138
|
+
this.stateMachine.transition("authenticated");
|
|
139
|
+
this.transport?.send({
|
|
140
|
+
type: "confirm",
|
|
141
|
+
sessionID: this.sessionId,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
this.fail(new Error("Failed to decrypt authentication data"));
|
|
146
|
+
this.restartIfContinuous();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async getCachedEntry(cached) {
|
|
150
|
+
try {
|
|
151
|
+
const res = await fetch(`${BASEURL}/session?session_id=${cached.sessionId}`);
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
throw new Error("Invalid session");
|
|
154
|
+
const data = await res.json();
|
|
155
|
+
const decrypted = this.crypto.decryptSymmetric({
|
|
156
|
+
encrypted: { cipher: data.cipher, nonce: data.nonce },
|
|
157
|
+
keyString: cached.sessionKey,
|
|
158
|
+
});
|
|
159
|
+
if (!decrypted) {
|
|
160
|
+
this.fail(new Error("Invalid session data"));
|
|
161
|
+
this.restartIfContinuous();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const result = JSON.parse(decrypted);
|
|
165
|
+
this.emit("success", result);
|
|
166
|
+
clearAuthSession();
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
this.fail(new Error("Session recovery failed"));
|
|
170
|
+
this.restartIfContinuous();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
attachVisibilityRecovery() {
|
|
174
|
+
this.visibilityHandler = async () => {
|
|
175
|
+
if (document.visibilityState !== "visible")
|
|
176
|
+
return;
|
|
177
|
+
const cached = getAuthSession();
|
|
178
|
+
if (!cached)
|
|
179
|
+
return;
|
|
180
|
+
await this.getCachedEntry(cached);
|
|
181
|
+
};
|
|
182
|
+
document.addEventListener("visibilitychange", this.visibilityHandler);
|
|
183
|
+
}
|
|
184
|
+
detachVisibilityRecovery() {
|
|
185
|
+
if (this.visibilityHandler) {
|
|
186
|
+
document.removeEventListener("visibilitychange", this.visibilityHandler);
|
|
187
|
+
this.visibilityHandler = undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
terminate(success) {
|
|
191
|
+
if (!this.transport)
|
|
192
|
+
return;
|
|
193
|
+
if (!success) {
|
|
194
|
+
this.transport?.send({
|
|
195
|
+
type: "client-terminated",
|
|
196
|
+
sessionID: this.sessionId,
|
|
197
|
+
projectId: this.config.projectId,
|
|
198
|
+
publicKey: this.config.publicKey,
|
|
199
|
+
authType: this.config.authType,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
setTimeout(() => {
|
|
203
|
+
this.transport?.close();
|
|
204
|
+
}, 150);
|
|
205
|
+
clearAuthSession();
|
|
206
|
+
this.resetSession();
|
|
207
|
+
if (!this.config.continuousMode) {
|
|
208
|
+
this.detachVisibilityRecovery();
|
|
209
|
+
}
|
|
210
|
+
this.stateMachine.transition(success ? "confirmed" : "terminated");
|
|
211
|
+
}
|
|
212
|
+
restartIfContinuous() {
|
|
213
|
+
if (!this.config.continuousMode)
|
|
214
|
+
return;
|
|
215
|
+
this.terminate(false);
|
|
216
|
+
this.stateMachine.transition("idle");
|
|
217
|
+
setTimeout(() => {
|
|
218
|
+
this.start();
|
|
219
|
+
}, 150);
|
|
220
|
+
}
|
|
221
|
+
resetSession() {
|
|
222
|
+
this.sessionId = "";
|
|
223
|
+
this.sessionKey = null;
|
|
224
|
+
}
|
|
225
|
+
destroy() {
|
|
226
|
+
this.detachVisibilityRecovery();
|
|
227
|
+
this.transport?.close();
|
|
228
|
+
this.listeners = {};
|
|
229
|
+
}
|
|
230
|
+
emit(event, payload) {
|
|
231
|
+
this.listeners[event]?.forEach((cb) => cb(payload));
|
|
232
|
+
}
|
|
233
|
+
fail(err) {
|
|
234
|
+
this.emit("error", err);
|
|
235
|
+
this.stateMachine.transition("error");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/engine/engine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -316,8 +316,10 @@ var PelicanAuthentication = class {
|
|
|
316
316
|
}
|
|
317
317
|
/** Main WebSocket message router */
|
|
318
318
|
handleMessage(msg) {
|
|
319
|
+
console.log(msg);
|
|
319
320
|
switch (msg.type) {
|
|
320
321
|
case "paired":
|
|
322
|
+
console.log("Paired");
|
|
321
323
|
this.stateMachine.transition("paired");
|
|
322
324
|
this.transport.send({
|
|
323
325
|
type: "authenticate",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utilities/crypto.ts","../src/utilities/storage.ts","../src/utilities/stateMachine.ts","../src/utilities/transport.ts","../src/constants.ts","../src/engine/engine.ts"],"names":["nacl","encodeBase64","decodeBase64","QRCode"],"mappings":"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","file":"index.js","sourcesContent":["import nacl from \"tweetnacl\";\nimport { decodeBase64, encodeBase64 } from \"tweetnacl-util\";\n\ninterface EncryptedMessage {\n cipher: string; // Base64 encoded\n nonce: string; // Base64 encoded\n}\n\nexport class CryptoService {\n generateSymmetricKey(): string {\n const key = nacl.randomBytes(32);\n return encodeBase64(key);\n }\n\n /**\n * Encrypt with symmetric key (secret-key encryption)\n * Use this when both parties share the same secret key\n * @param plaintext - Message to encrypt\n * @param keyString - Symmetric key (base64)\n * @returns Encrypted message with nonce\n */\n encryptSymmetric({\n plaintext,\n keyString,\n }: {\n plaintext: string;\n keyString: string;\n }): EncryptedMessage {\n const key = decodeBase64(keyString);\n const nonce = nacl.randomBytes(24);\n\n const messageBytes = new TextEncoder().encode(plaintext);\n\n const ciphertext = nacl.secretbox(messageBytes, nonce, key);\n\n return {\n cipher: encodeBase64(ciphertext),\n nonce: encodeBase64(nonce),\n };\n }\n\n /**\n * Decrypt with symmetric key\n * @param encrypted - Encrypted message with nonce\n * @param keyString - Symmetric key (base64)\n * @returns Decrypted plaintext\n */\n decryptSymmetric({\n encrypted,\n keyString,\n }: {\n encrypted: EncryptedMessage;\n keyString: string;\n }): string | null {\n try {\n const key = decodeBase64(keyString);\n const ciphertextBytes = decodeBase64(encrypted.cipher);\n const nonceBytes = decodeBase64(encrypted.nonce);\n\n const decrypted = nacl.secretbox.open(ciphertextBytes, nonceBytes, key);\n\n if (!decrypted) {\n throw new Error(\"Decryption failed - invalid key or corrupted data\");\n }\n\n const decoded = new TextDecoder().decode(decrypted);\n return decoded;\n } catch (error) {\n console.error(\"Decryption failed\", error);\n return null;\n }\n }\n}\n\nexport default CryptoService;\nexport type { EncryptedMessage };\n","// hybridStorage.ts\n/**\n * Hybrid storage: Try sessionStorage first, fallback to memory\n * Best of both worlds - survives page refresh but auto-cleans\n */\n\ninterface StorageOptions {\n ttlMs?: number;\n useSessionStorage?: boolean;\n}\n\nclass AuthStorage {\n private readonly prefix = \"pelican_auth_\";\n private readonly defaultTTL = 5 * 60 * 1000; // 5 minutes\n private memoryCache: Map<string, { value: any; expiresAt: number }> =\n new Map();\n\n /**\n * Store auth session with automatic cleanup\n */\n set(key: string, value: any, options: StorageOptions = {}): void {\n const { ttlMs = this.defaultTTL, useSessionStorage = true } = options;\n\n const expiresAt = Date.now() + ttlMs;\n const data = { value, expiresAt };\n\n // Try sessionStorage first\n if (useSessionStorage) {\n try {\n sessionStorage.setItem(`${this.prefix}${key}`, JSON.stringify(data));\n } catch (error) {\n console.warn(\"SessionStorage unavailable, using memory:\", error);\n this.setMemory(key, data);\n }\n } else {\n this.setMemory(key, data);\n }\n }\n\n /**\n * Get stored value if not expired\n */\n get(key: string): any | null {\n // Try sessionStorage first\n try {\n const stored = sessionStorage.getItem(`${this.prefix}${key}`);\n if (stored) {\n const data = JSON.parse(stored);\n\n // Check expiration\n if (Date.now() > data.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return data.value;\n }\n } catch (error) {\n // Fallback to memory\n }\n\n // Try memory cache\n return this.getMemory(key);\n }\n\n /**\n * Remove specific key\n */\n remove(key: string): void {\n try {\n sessionStorage.removeItem(`${this.prefix}${key}`);\n } catch (error) {\n // Ignore\n }\n this.memoryCache.delete(key);\n }\n\n /**\n * Clear all auth data\n */\n clear(): void {\n // Clear sessionStorage\n try {\n Object.keys(sessionStorage).forEach((key) => {\n if (key.startsWith(this.prefix)) {\n sessionStorage.removeItem(key);\n }\n });\n } catch (error) {\n // Ignore\n }\n\n // Clear memory\n this.memoryCache.clear();\n }\n\n // ========================================\n // Memory cache helpers\n // ========================================\n\n private setMemory(\n key: string,\n data: { value: any; expiresAt: number }\n ): void {\n this.memoryCache.set(key, data);\n\n // Schedule cleanup\n const ttl = data.expiresAt - Date.now();\n setTimeout(() => {\n this.memoryCache.delete(key);\n }, ttl);\n }\n\n private getMemory(key: string): any | null {\n const data = this.memoryCache.get(key);\n if (!data) return null;\n\n if (Date.now() > data.expiresAt) {\n this.memoryCache.delete(key);\n return null;\n }\n\n return data.value;\n }\n}\n\n// Singleton instance\nconst storage = new AuthStorage();\n\n// ========================================\n// Convenience functions for auth flow\n// ========================================\n\nexport interface AuthSession {\n sessionId: string;\n sessionKey: string;\n}\n\nexport const storeAuthSession = (\n sessionId: string,\n sessionKey: string,\n ttlMs?: number\n): void => {\n storage.set(\"session\", { sessionId, sessionKey }, { ttlMs });\n};\n\nexport const getAuthSession = (): AuthSession | null => {\n return storage.get(\"session\");\n};\n\nexport const clearAuthSession = (): void => {\n storage.remove(\"session\");\n};\n\nexport const clearAllAuthData = (): void => {\n storage.clear();\n};\n\nexport default storage;\n","import type { PelicanAuthState } from \"../types/types\";\n\nexport class StateMachine {\n private state: PelicanAuthState = \"idle\";\n private listeners = new Set<(s: PelicanAuthState) => void>();\n\n get current() {\n return this.state;\n }\n\n transition(next: PelicanAuthState) {\n this.state = next;\n this.listeners.forEach((l) => l(next));\n }\n\n subscribe(fn: (s: PelicanAuthState) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n","import { ISocketMessage } from \"../types/types\";\n\ntype TransportHandlers = {\n onOpen?: () => void;\n onMessage?: (msg: ISocketMessage) => void;\n onError?: (err: Event) => void;\n onClose?: (ev: CloseEvent) => void;\n};\n\ninterface PelicanMessageEvent extends MessageEvent {\n data: ISocketMessage;\n}\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => this.handlers.onOpen?.();\n this.socket.onmessage = (e: PelicanMessageEvent) =>\n this.handlers.onMessage?.(e.data);\n this.socket.onerror = (e: Event) => this.handlers.onError?.(e);\n this.socket.onclose = (e: CloseEvent) => this.handlers.onClose?.(e);\n }\n\n send(payload: ISocketMessage) {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(payload));\n }\n }\n\n close() {\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://localhost:8080\";\n","import CryptoService from \"../utilities/crypto\";\nimport QRCode from \"qrcode\";\nimport {\n PelicanAuthConfig,\n PelicanAuthEventMap,\n ISocketMessage,\n IdentityResult,\n} from \"../types/types\";\nimport {\n storeAuthSession,\n getAuthSession,\n clearAuthSession,\n AuthSession,\n} from \"../utilities/storage\";\nimport { StateMachine } from \"../utilities/stateMachine\";\nimport { Transport } from \"../utilities/transport\";\n\nimport { BASEURL } from \"../constants\";\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * PelicanAuth SDK\n * Handles cross-device authentication via WebSockets and E2EE (End-to-End Encryption).\n */\nexport class PelicanAuthentication {\n private readonly crypto = new CryptoService();\n private readonly stateMachine = new StateMachine();\n\n private transport?: Transport;\n private sessionId = \"\";\n private sessionKey: string | null = null;\n\n private visibilityHandler?: () => void;\n\n private listeners: {\n [K in keyof PelicanAuthEventMap]?: Set<Listener<any>>;\n } = {};\n\n private readonly config: Required<PelicanAuthConfig>;\n\n constructor(config: PelicanAuthConfig) {\n if (!config.publicKey) throw new Error(\"Missing publicKey\");\n if (!config.projectId) throw new Error(\"Missing projectId\");\n if (!config.authType) throw new Error(\"Missing authType\");\n\n this.config = {\n continuousMode: false,\n forceQRCode: false,\n ...config,\n };\n\n // Sync internal state machine changes with the 'state' event\n this.stateMachine.subscribe((s) => this.emit(\"state\", s));\n this.attachVisibilityRecovery();\n }\n\n /* -------------------- public API -------------------- */\n\n /**\n * Subscribe to SDK events (qr, deeplink, success, error, state)\n * @returns Unsubscribe function\n */\n on<K extends keyof PelicanAuthEventMap>(\n event: K,\n cb: Listener<PelicanAuthEventMap[K]>\n ) {\n this.listeners[event] ??= new Set();\n this.listeners[event]!.add(cb);\n return () => this.listeners[event]!.delete(cb);\n }\n\n /**\n * Initializes the authentication flow.\n * Fetches a relay, establishes a WebSocket, and generates the E2EE session key.\n */\n async start() {\n if (this.stateMachine.current !== \"idle\") return;\n\n this.resetSession();\n this.stateMachine.transition(\"initializing\");\n\n try {\n const relay = await this.fetchRelayUrl();\n\n // Generate a fresh symmetric key for this specific session\n this.sessionKey = this.crypto.generateSymmetricKey();\n this.sessionId = crypto.randomUUID() + crypto.randomUUID();\n\n this.transport = new Transport({\n onOpen: () => {\n // Register this browser session with the relay\n this.transport!.send({\n type: \"register\",\n sessionID: this.sessionId,\n ...this.config,\n });\n this.stateMachine.transition(\"awaiting-pair\");\n },\n onMessage: (msg: ISocketMessage) => this.handleMessage(msg),\n onError: () => this.fail(new Error(\"WebSocket connection failed\")),\n });\n\n this.transport.connect(relay);\n await this.emitEntryPoint();\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(\"Start failed\"));\n }\n }\n\n /**\n * Manually stops the authentication process and closes connections.\n */\n stop() {\n this.terminate(false);\n }\n\n /* -------------------- internals -------------------- */\n\n /** Fetches the WebSocket Relay URL from the backend */\n private async fetchRelayUrl(): Promise<string> {\n const { publicKey, projectId, authType } = this.config;\n const res = await fetch(\n `${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`\n );\n\n if (!res.ok) {\n throw new Error(\"Failed to fetch relay URL\");\n }\n\n const json = await res.json();\n return json.relay_url;\n }\n\n /**\n * Decides whether to show a QR code (Desktop) or a Deep Link (Mobile).\n */\n private async emitEntryPoint() {\n const payload = {\n sessionID: this.sessionId,\n sessionKey: this.sessionKey,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n projectId: this.config.projectId,\n url: window.location.href,\n };\n\n const shouldUseQR =\n this.config.forceQRCode ||\n !/Android|iPhone|iPad/i.test(navigator.userAgent);\n\n if (!shouldUseQR && this.sessionKey) {\n // For mobile: Store session locally so we can recover when the user returns from the app\n storeAuthSession(this.sessionId, this.sessionKey, 5 * 60_000);\n this.emit(\n \"deeplink\",\n `pelicanvault://auth/deep-link?data=${encodeURIComponent(\n JSON.stringify(payload)\n )}`\n );\n } else {\n // For desktop: Generate a QR code for the mobile app to scan\n const qr = await QRCode.toDataURL(JSON.stringify(payload));\n this.emit(\"qr\", qr);\n }\n }\n\n /** Main WebSocket message router */\n private handleMessage(msg: ISocketMessage) {\n switch (msg.type) {\n case \"paired\":\n // The mobile device has scanned the QR/opened the link\n this.stateMachine.transition(\"paired\");\n this.transport!.send({\n type: \"authenticate\",\n sessionID: this.sessionId,\n ...this.config,\n url: window.location.href,\n });\n return;\n\n case \"phone-auth-success\":\n // Mobile device successfully authenticated and sent encrypted credentials\n this.handleAuthSuccess(msg);\n return;\n\n case \"phone-terminated\":\n this.fail(new Error(\"Authenticating device terminated the connection\"));\n this.restartIfContinuous();\n return;\n\n case \"confirmed\":\n // Handshake complete\n this.terminate(true);\n this.restartIfContinuous();\n return;\n }\n }\n\n /**\n * Decrypts the identity payload received from the phone using the session key.\n */\n private handleAuthSuccess(msg: ISocketMessage) {\n if (!this.sessionKey || !msg.cipher || !msg.nonce) {\n this.fail(new Error(\"Invalid authentication payload\"));\n this.restartIfContinuous();\n return;\n }\n\n try {\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: msg.cipher, nonce: msg.nonce },\n keyString: this.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid authentication data\"));\n this.restartIfContinuous();\n return;\n }\n\n const result: IdentityResult = JSON.parse(decrypted);\n\n this.emit(\"success\", result);\n this.stateMachine.transition(\"authenticated\");\n\n // Signal to the relay/phone that we have received and decrypted the data\n this.transport?.send({\n type: \"confirm\",\n sessionID: this.sessionId,\n });\n } catch {\n this.fail(new Error(\"Failed to decrypt authentication data\"));\n this.restartIfContinuous();\n }\n }\n\n /**\n * Logic to handle users returning to the browser tab after using the mobile app.\n * Checks the server for a completed session that might have finished while in background.\n */\n\n private async getCachedEntry(cached: AuthSession) {\n try {\n const res = await fetch(\n `${BASEURL}/session?session_id=${cached.sessionId}`\n );\n\n if (!res.ok) throw new Error(\"Invalid session\");\n\n const data = await res.json();\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: data.cipher, nonce: data.nonce },\n keyString: cached.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid session data\"));\n this.restartIfContinuous();\n return;\n }\n const result: IdentityResult = JSON.parse(decrypted);\n this.emit(\"success\", result);\n clearAuthSession();\n } catch {\n this.fail(new Error(\"Session recovery failed\"));\n this.restartIfContinuous();\n }\n }\n\n private attachVisibilityRecovery() {\n this.visibilityHandler = async () => {\n if (document.visibilityState !== \"visible\") return;\n\n const cached = getAuthSession();\n if (!cached) return;\n await this.getCachedEntry(cached);\n };\n\n document.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n private detachVisibilityRecovery() {\n if (this.visibilityHandler) {\n document.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = undefined;\n }\n }\n\n /** Cleans up the current session state */\n private terminate(success: boolean) {\n if (!this.transport) return;\n if (!success) {\n this.transport?.send({\n type: \"client-terminated\",\n sessionID: this.sessionId,\n projectId: this.config.projectId,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n });\n }\n\n setTimeout(() => {\n this.transport?.close();\n }, 150);\n\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"terminated\");\n }\n\n private restartIfContinuous() {\n if (!this.config.continuousMode) return;\n this.terminate(false);\n this.stateMachine.transition(\"idle\");\n\n setTimeout(() => {\n this.start();\n }, 150);\n }\n\n private resetSession() {\n this.sessionId = \"\";\n this.sessionKey = null;\n }\n\n /** Completely destroys the instance and removes all listeners */\n destroy() {\n this.detachVisibilityRecovery();\n this.transport?.close();\n this.listeners = {};\n }\n\n private emit<K extends keyof PelicanAuthEventMap>(\n event: K,\n payload: PelicanAuthEventMap[K]\n ) {\n this.listeners[event]?.forEach((cb) => cb(payload));\n }\n\n private fail(err: Error) {\n this.emit(\"error\", err);\n this.stateMachine.transition(\"error\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utilities/crypto.ts","../src/utilities/storage.ts","../src/utilities/stateMachine.ts","../src/utilities/transport.ts","../src/constants.ts","../src/engine/engine.ts"],"names":["nacl","encodeBase64","decodeBase64","QRCode"],"mappings":"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","file":"index.js","sourcesContent":["import nacl from \"tweetnacl\";\nimport { decodeBase64, encodeBase64 } from \"tweetnacl-util\";\n\ninterface EncryptedMessage {\n cipher: string; // Base64 encoded\n nonce: string; // Base64 encoded\n}\n\nexport class CryptoService {\n generateSymmetricKey(): string {\n const key = nacl.randomBytes(32);\n return encodeBase64(key);\n }\n\n /**\n * Encrypt with symmetric key (secret-key encryption)\n * Use this when both parties share the same secret key\n * @param plaintext - Message to encrypt\n * @param keyString - Symmetric key (base64)\n * @returns Encrypted message with nonce\n */\n encryptSymmetric({\n plaintext,\n keyString,\n }: {\n plaintext: string;\n keyString: string;\n }): EncryptedMessage {\n const key = decodeBase64(keyString);\n const nonce = nacl.randomBytes(24);\n\n const messageBytes = new TextEncoder().encode(plaintext);\n\n const ciphertext = nacl.secretbox(messageBytes, nonce, key);\n\n return {\n cipher: encodeBase64(ciphertext),\n nonce: encodeBase64(nonce),\n };\n }\n\n /**\n * Decrypt with symmetric key\n * @param encrypted - Encrypted message with nonce\n * @param keyString - Symmetric key (base64)\n * @returns Decrypted plaintext\n */\n decryptSymmetric({\n encrypted,\n keyString,\n }: {\n encrypted: EncryptedMessage;\n keyString: string;\n }): string | null {\n try {\n const key = decodeBase64(keyString);\n const ciphertextBytes = decodeBase64(encrypted.cipher);\n const nonceBytes = decodeBase64(encrypted.nonce);\n\n const decrypted = nacl.secretbox.open(ciphertextBytes, nonceBytes, key);\n\n if (!decrypted) {\n throw new Error(\"Decryption failed - invalid key or corrupted data\");\n }\n\n const decoded = new TextDecoder().decode(decrypted);\n return decoded;\n } catch (error) {\n console.error(\"Decryption failed\", error);\n return null;\n }\n }\n}\n\nexport default CryptoService;\nexport type { EncryptedMessage };\n","// hybridStorage.ts\n/**\n * Hybrid storage: Try sessionStorage first, fallback to memory\n * Best of both worlds - survives page refresh but auto-cleans\n */\n\ninterface StorageOptions {\n ttlMs?: number;\n useSessionStorage?: boolean;\n}\n\nclass AuthStorage {\n private readonly prefix = \"pelican_auth_\";\n private readonly defaultTTL = 5 * 60 * 1000; // 5 minutes\n private memoryCache: Map<string, { value: any; expiresAt: number }> =\n new Map();\n\n /**\n * Store auth session with automatic cleanup\n */\n set(key: string, value: any, options: StorageOptions = {}): void {\n const { ttlMs = this.defaultTTL, useSessionStorage = true } = options;\n\n const expiresAt = Date.now() + ttlMs;\n const data = { value, expiresAt };\n\n // Try sessionStorage first\n if (useSessionStorage) {\n try {\n sessionStorage.setItem(`${this.prefix}${key}`, JSON.stringify(data));\n } catch (error) {\n console.warn(\"SessionStorage unavailable, using memory:\", error);\n this.setMemory(key, data);\n }\n } else {\n this.setMemory(key, data);\n }\n }\n\n /**\n * Get stored value if not expired\n */\n get(key: string): any | null {\n // Try sessionStorage first\n try {\n const stored = sessionStorage.getItem(`${this.prefix}${key}`);\n if (stored) {\n const data = JSON.parse(stored);\n\n // Check expiration\n if (Date.now() > data.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return data.value;\n }\n } catch (error) {\n // Fallback to memory\n }\n\n // Try memory cache\n return this.getMemory(key);\n }\n\n /**\n * Remove specific key\n */\n remove(key: string): void {\n try {\n sessionStorage.removeItem(`${this.prefix}${key}`);\n } catch (error) {\n // Ignore\n }\n this.memoryCache.delete(key);\n }\n\n /**\n * Clear all auth data\n */\n clear(): void {\n // Clear sessionStorage\n try {\n Object.keys(sessionStorage).forEach((key) => {\n if (key.startsWith(this.prefix)) {\n sessionStorage.removeItem(key);\n }\n });\n } catch (error) {\n // Ignore\n }\n\n // Clear memory\n this.memoryCache.clear();\n }\n\n // ========================================\n // Memory cache helpers\n // ========================================\n\n private setMemory(\n key: string,\n data: { value: any; expiresAt: number }\n ): void {\n this.memoryCache.set(key, data);\n\n // Schedule cleanup\n const ttl = data.expiresAt - Date.now();\n setTimeout(() => {\n this.memoryCache.delete(key);\n }, ttl);\n }\n\n private getMemory(key: string): any | null {\n const data = this.memoryCache.get(key);\n if (!data) return null;\n\n if (Date.now() > data.expiresAt) {\n this.memoryCache.delete(key);\n return null;\n }\n\n return data.value;\n }\n}\n\n// Singleton instance\nconst storage = new AuthStorage();\n\n// ========================================\n// Convenience functions for auth flow\n// ========================================\n\nexport interface AuthSession {\n sessionId: string;\n sessionKey: string;\n}\n\nexport const storeAuthSession = (\n sessionId: string,\n sessionKey: string,\n ttlMs?: number\n): void => {\n storage.set(\"session\", { sessionId, sessionKey }, { ttlMs });\n};\n\nexport const getAuthSession = (): AuthSession | null => {\n return storage.get(\"session\");\n};\n\nexport const clearAuthSession = (): void => {\n storage.remove(\"session\");\n};\n\nexport const clearAllAuthData = (): void => {\n storage.clear();\n};\n\nexport default storage;\n","import type { PelicanAuthState } from \"../types/types\";\n\nexport class StateMachine {\n private state: PelicanAuthState = \"idle\";\n private listeners = new Set<(s: PelicanAuthState) => void>();\n\n get current() {\n return this.state;\n }\n\n transition(next: PelicanAuthState) {\n this.state = next;\n this.listeners.forEach((l) => l(next));\n }\n\n subscribe(fn: (s: PelicanAuthState) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n","import { ISocketMessage } from \"../types/types\";\n\ntype TransportHandlers = {\n onOpen?: () => void;\n onMessage?: (msg: ISocketMessage) => void;\n onError?: (err: Event) => void;\n onClose?: (ev: CloseEvent) => void;\n};\n\ninterface PelicanMessageEvent extends MessageEvent {\n data: ISocketMessage;\n}\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => this.handlers.onOpen?.();\n this.socket.onmessage = (e: PelicanMessageEvent) =>\n this.handlers.onMessage?.(e.data);\n this.socket.onerror = (e: Event) => this.handlers.onError?.(e);\n this.socket.onclose = (e: CloseEvent) => this.handlers.onClose?.(e);\n }\n\n send(payload: ISocketMessage) {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(payload));\n }\n }\n\n close() {\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://localhost:8080\";\n","import CryptoService from \"../utilities/crypto\";\nimport QRCode from \"qrcode\";\nimport {\n PelicanAuthConfig,\n PelicanAuthEventMap,\n ISocketMessage,\n IdentityResult,\n} from \"../types/types\";\nimport {\n storeAuthSession,\n getAuthSession,\n clearAuthSession,\n AuthSession,\n} from \"../utilities/storage\";\nimport { StateMachine } from \"../utilities/stateMachine\";\nimport { Transport } from \"../utilities/transport\";\n\nimport { BASEURL } from \"../constants\";\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * PelicanAuth SDK\n * Handles cross-device authentication via WebSockets and E2EE (End-to-End Encryption).\n */\nexport class PelicanAuthentication {\n private readonly crypto = new CryptoService();\n private readonly stateMachine = new StateMachine();\n\n private transport?: Transport;\n private sessionId = \"\";\n private sessionKey: string | null = null;\n\n private visibilityHandler?: () => void;\n\n private listeners: {\n [K in keyof PelicanAuthEventMap]?: Set<Listener<any>>;\n } = {};\n\n private readonly config: Required<PelicanAuthConfig>;\n\n constructor(config: PelicanAuthConfig) {\n if (!config.publicKey) throw new Error(\"Missing publicKey\");\n if (!config.projectId) throw new Error(\"Missing projectId\");\n if (!config.authType) throw new Error(\"Missing authType\");\n\n this.config = {\n continuousMode: false,\n forceQRCode: false,\n ...config,\n };\n\n // Sync internal state machine changes with the 'state' event\n this.stateMachine.subscribe((s) => this.emit(\"state\", s));\n this.attachVisibilityRecovery();\n }\n\n /* -------------------- public API -------------------- */\n\n /**\n * Subscribe to SDK events (qr, deeplink, success, error, state)\n * @returns Unsubscribe function\n */\n on<K extends keyof PelicanAuthEventMap>(\n event: K,\n cb: Listener<PelicanAuthEventMap[K]>\n ) {\n this.listeners[event] ??= new Set();\n this.listeners[event]!.add(cb);\n return () => this.listeners[event]!.delete(cb);\n }\n\n /**\n * Initializes the authentication flow.\n * Fetches a relay, establishes a WebSocket, and generates the E2EE session key.\n */\n async start() {\n if (this.stateMachine.current !== \"idle\") return;\n\n this.resetSession();\n this.stateMachine.transition(\"initializing\");\n\n try {\n const relay = await this.fetchRelayUrl();\n\n // Generate a fresh symmetric key for this specific session\n this.sessionKey = this.crypto.generateSymmetricKey();\n this.sessionId = crypto.randomUUID() + crypto.randomUUID();\n\n this.transport = new Transport({\n onOpen: () => {\n // Register this browser session with the relay\n this.transport!.send({\n type: \"register\",\n sessionID: this.sessionId,\n ...this.config,\n });\n this.stateMachine.transition(\"awaiting-pair\");\n },\n onMessage: (msg: ISocketMessage) => this.handleMessage(msg),\n onError: () => this.fail(new Error(\"WebSocket connection failed\")),\n });\n\n this.transport.connect(relay);\n await this.emitEntryPoint();\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(\"Start failed\"));\n }\n }\n\n /**\n * Manually stops the authentication process and closes connections.\n */\n stop() {\n this.terminate(false);\n }\n\n /* -------------------- internals -------------------- */\n\n /** Fetches the WebSocket Relay URL from the backend */\n private async fetchRelayUrl(): Promise<string> {\n const { publicKey, projectId, authType } = this.config;\n const res = await fetch(\n `${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`\n );\n\n if (!res.ok) {\n throw new Error(\"Failed to fetch relay URL\");\n }\n\n const json = await res.json();\n return json.relay_url;\n }\n\n /**\n * Decides whether to show a QR code (Desktop) or a Deep Link (Mobile).\n */\n private async emitEntryPoint() {\n const payload = {\n sessionID: this.sessionId,\n sessionKey: this.sessionKey,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n projectId: this.config.projectId,\n url: window.location.href,\n };\n\n const shouldUseQR =\n this.config.forceQRCode ||\n !/Android|iPhone|iPad/i.test(navigator.userAgent);\n\n if (!shouldUseQR && this.sessionKey) {\n // For mobile: Store session locally so we can recover when the user returns from the app\n storeAuthSession(this.sessionId, this.sessionKey, 5 * 60_000);\n this.emit(\n \"deeplink\",\n `pelicanvault://auth/deep-link?data=${encodeURIComponent(\n JSON.stringify(payload)\n )}`\n );\n } else {\n // For desktop: Generate a QR code for the mobile app to scan\n const qr = await QRCode.toDataURL(JSON.stringify(payload));\n this.emit(\"qr\", qr);\n }\n }\n\n /** Main WebSocket message router */\n private handleMessage(msg: ISocketMessage) {\n console.log(msg);\n\n switch (msg.type) {\n case \"paired\":\n console.log(\"Paired\");\n\n this.stateMachine.transition(\"paired\");\n this.transport!.send({\n type: \"authenticate\",\n sessionID: this.sessionId,\n ...this.config,\n url: window.location.href,\n });\n return;\n\n case \"phone-auth-success\":\n // Mobile device successfully authenticated and sent encrypted credentials\n this.handleAuthSuccess(msg);\n return;\n\n case \"phone-terminated\":\n this.fail(new Error(\"Authenticating device terminated the connection\"));\n this.restartIfContinuous();\n return;\n\n case \"confirmed\":\n // Handshake complete\n this.terminate(true);\n this.restartIfContinuous();\n return;\n }\n }\n\n /**\n * Decrypts the identity payload received from the phone using the session key.\n */\n private handleAuthSuccess(msg: ISocketMessage) {\n if (!this.sessionKey || !msg.cipher || !msg.nonce) {\n this.fail(new Error(\"Invalid authentication payload\"));\n this.restartIfContinuous();\n return;\n }\n\n try {\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: msg.cipher, nonce: msg.nonce },\n keyString: this.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid authentication data\"));\n this.restartIfContinuous();\n return;\n }\n\n const result: IdentityResult = JSON.parse(decrypted);\n\n this.emit(\"success\", result);\n this.stateMachine.transition(\"authenticated\");\n\n // Signal to the relay/phone that we have received and decrypted the data\n this.transport?.send({\n type: \"confirm\",\n sessionID: this.sessionId,\n });\n } catch {\n this.fail(new Error(\"Failed to decrypt authentication data\"));\n this.restartIfContinuous();\n }\n }\n\n /**\n * Logic to handle users returning to the browser tab after using the mobile app.\n * Checks the server for a completed session that might have finished while in background.\n */\n\n private async getCachedEntry(cached: AuthSession) {\n try {\n const res = await fetch(\n `${BASEURL}/session?session_id=${cached.sessionId}`\n );\n\n if (!res.ok) throw new Error(\"Invalid session\");\n\n const data = await res.json();\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: data.cipher, nonce: data.nonce },\n keyString: cached.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid session data\"));\n this.restartIfContinuous();\n return;\n }\n const result: IdentityResult = JSON.parse(decrypted);\n this.emit(\"success\", result);\n clearAuthSession();\n } catch {\n this.fail(new Error(\"Session recovery failed\"));\n this.restartIfContinuous();\n }\n }\n\n private attachVisibilityRecovery() {\n this.visibilityHandler = async () => {\n if (document.visibilityState !== \"visible\") return;\n\n const cached = getAuthSession();\n if (!cached) return;\n await this.getCachedEntry(cached);\n };\n\n document.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n private detachVisibilityRecovery() {\n if (this.visibilityHandler) {\n document.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = undefined;\n }\n }\n\n /** Cleans up the current session state */\n private terminate(success: boolean) {\n if (!this.transport) return;\n if (!success) {\n this.transport?.send({\n type: \"client-terminated\",\n sessionID: this.sessionId,\n projectId: this.config.projectId,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n });\n }\n\n setTimeout(() => {\n this.transport?.close();\n }, 150);\n\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"terminated\");\n }\n\n private restartIfContinuous() {\n if (!this.config.continuousMode) return;\n this.terminate(false);\n this.stateMachine.transition(\"idle\");\n\n setTimeout(() => {\n this.start();\n }, 150);\n }\n\n private resetSession() {\n this.sessionId = \"\";\n this.sessionKey = null;\n }\n\n /** Completely destroys the instance and removes all listeners */\n destroy() {\n this.detachVisibilityRecovery();\n this.transport?.close();\n this.listeners = {};\n }\n\n private emit<K extends keyof PelicanAuthEventMap>(\n event: K,\n payload: PelicanAuthEventMap[K]\n ) {\n this.listeners[event]?.forEach((cb) => cb(payload));\n }\n\n private fail(err: Error) {\n this.emit(\"error\", err);\n this.stateMachine.transition(\"error\");\n }\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -309,8 +309,10 @@ var PelicanAuthentication = class {
|
|
|
309
309
|
}
|
|
310
310
|
/** Main WebSocket message router */
|
|
311
311
|
handleMessage(msg) {
|
|
312
|
+
console.log(msg);
|
|
312
313
|
switch (msg.type) {
|
|
313
314
|
case "paired":
|
|
315
|
+
console.log("Paired");
|
|
314
316
|
this.stateMachine.transition("paired");
|
|
315
317
|
this.transport.send({
|
|
316
318
|
type: "authenticate",
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utilities/crypto.ts","../src/utilities/storage.ts","../src/utilities/stateMachine.ts","../src/utilities/transport.ts","../src/constants.ts","../src/engine/engine.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import nacl from \"tweetnacl\";\nimport { decodeBase64, encodeBase64 } from \"tweetnacl-util\";\n\ninterface EncryptedMessage {\n cipher: string; // Base64 encoded\n nonce: string; // Base64 encoded\n}\n\nexport class CryptoService {\n generateSymmetricKey(): string {\n const key = nacl.randomBytes(32);\n return encodeBase64(key);\n }\n\n /**\n * Encrypt with symmetric key (secret-key encryption)\n * Use this when both parties share the same secret key\n * @param plaintext - Message to encrypt\n * @param keyString - Symmetric key (base64)\n * @returns Encrypted message with nonce\n */\n encryptSymmetric({\n plaintext,\n keyString,\n }: {\n plaintext: string;\n keyString: string;\n }): EncryptedMessage {\n const key = decodeBase64(keyString);\n const nonce = nacl.randomBytes(24);\n\n const messageBytes = new TextEncoder().encode(plaintext);\n\n const ciphertext = nacl.secretbox(messageBytes, nonce, key);\n\n return {\n cipher: encodeBase64(ciphertext),\n nonce: encodeBase64(nonce),\n };\n }\n\n /**\n * Decrypt with symmetric key\n * @param encrypted - Encrypted message with nonce\n * @param keyString - Symmetric key (base64)\n * @returns Decrypted plaintext\n */\n decryptSymmetric({\n encrypted,\n keyString,\n }: {\n encrypted: EncryptedMessage;\n keyString: string;\n }): string | null {\n try {\n const key = decodeBase64(keyString);\n const ciphertextBytes = decodeBase64(encrypted.cipher);\n const nonceBytes = decodeBase64(encrypted.nonce);\n\n const decrypted = nacl.secretbox.open(ciphertextBytes, nonceBytes, key);\n\n if (!decrypted) {\n throw new Error(\"Decryption failed - invalid key or corrupted data\");\n }\n\n const decoded = new TextDecoder().decode(decrypted);\n return decoded;\n } catch (error) {\n console.error(\"Decryption failed\", error);\n return null;\n }\n }\n}\n\nexport default CryptoService;\nexport type { EncryptedMessage };\n","// hybridStorage.ts\n/**\n * Hybrid storage: Try sessionStorage first, fallback to memory\n * Best of both worlds - survives page refresh but auto-cleans\n */\n\ninterface StorageOptions {\n ttlMs?: number;\n useSessionStorage?: boolean;\n}\n\nclass AuthStorage {\n private readonly prefix = \"pelican_auth_\";\n private readonly defaultTTL = 5 * 60 * 1000; // 5 minutes\n private memoryCache: Map<string, { value: any; expiresAt: number }> =\n new Map();\n\n /**\n * Store auth session with automatic cleanup\n */\n set(key: string, value: any, options: StorageOptions = {}): void {\n const { ttlMs = this.defaultTTL, useSessionStorage = true } = options;\n\n const expiresAt = Date.now() + ttlMs;\n const data = { value, expiresAt };\n\n // Try sessionStorage first\n if (useSessionStorage) {\n try {\n sessionStorage.setItem(`${this.prefix}${key}`, JSON.stringify(data));\n } catch (error) {\n console.warn(\"SessionStorage unavailable, using memory:\", error);\n this.setMemory(key, data);\n }\n } else {\n this.setMemory(key, data);\n }\n }\n\n /**\n * Get stored value if not expired\n */\n get(key: string): any | null {\n // Try sessionStorage first\n try {\n const stored = sessionStorage.getItem(`${this.prefix}${key}`);\n if (stored) {\n const data = JSON.parse(stored);\n\n // Check expiration\n if (Date.now() > data.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return data.value;\n }\n } catch (error) {\n // Fallback to memory\n }\n\n // Try memory cache\n return this.getMemory(key);\n }\n\n /**\n * Remove specific key\n */\n remove(key: string): void {\n try {\n sessionStorage.removeItem(`${this.prefix}${key}`);\n } catch (error) {\n // Ignore\n }\n this.memoryCache.delete(key);\n }\n\n /**\n * Clear all auth data\n */\n clear(): void {\n // Clear sessionStorage\n try {\n Object.keys(sessionStorage).forEach((key) => {\n if (key.startsWith(this.prefix)) {\n sessionStorage.removeItem(key);\n }\n });\n } catch (error) {\n // Ignore\n }\n\n // Clear memory\n this.memoryCache.clear();\n }\n\n // ========================================\n // Memory cache helpers\n // ========================================\n\n private setMemory(\n key: string,\n data: { value: any; expiresAt: number }\n ): void {\n this.memoryCache.set(key, data);\n\n // Schedule cleanup\n const ttl = data.expiresAt - Date.now();\n setTimeout(() => {\n this.memoryCache.delete(key);\n }, ttl);\n }\n\n private getMemory(key: string): any | null {\n const data = this.memoryCache.get(key);\n if (!data) return null;\n\n if (Date.now() > data.expiresAt) {\n this.memoryCache.delete(key);\n return null;\n }\n\n return data.value;\n }\n}\n\n// Singleton instance\nconst storage = new AuthStorage();\n\n// ========================================\n// Convenience functions for auth flow\n// ========================================\n\nexport interface AuthSession {\n sessionId: string;\n sessionKey: string;\n}\n\nexport const storeAuthSession = (\n sessionId: string,\n sessionKey: string,\n ttlMs?: number\n): void => {\n storage.set(\"session\", { sessionId, sessionKey }, { ttlMs });\n};\n\nexport const getAuthSession = (): AuthSession | null => {\n return storage.get(\"session\");\n};\n\nexport const clearAuthSession = (): void => {\n storage.remove(\"session\");\n};\n\nexport const clearAllAuthData = (): void => {\n storage.clear();\n};\n\nexport default storage;\n","import type { PelicanAuthState } from \"../types/types\";\n\nexport class StateMachine {\n private state: PelicanAuthState = \"idle\";\n private listeners = new Set<(s: PelicanAuthState) => void>();\n\n get current() {\n return this.state;\n }\n\n transition(next: PelicanAuthState) {\n this.state = next;\n this.listeners.forEach((l) => l(next));\n }\n\n subscribe(fn: (s: PelicanAuthState) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n","import { ISocketMessage } from \"../types/types\";\n\ntype TransportHandlers = {\n onOpen?: () => void;\n onMessage?: (msg: ISocketMessage) => void;\n onError?: (err: Event) => void;\n onClose?: (ev: CloseEvent) => void;\n};\n\ninterface PelicanMessageEvent extends MessageEvent {\n data: ISocketMessage;\n}\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => this.handlers.onOpen?.();\n this.socket.onmessage = (e: PelicanMessageEvent) =>\n this.handlers.onMessage?.(e.data);\n this.socket.onerror = (e: Event) => this.handlers.onError?.(e);\n this.socket.onclose = (e: CloseEvent) => this.handlers.onClose?.(e);\n }\n\n send(payload: ISocketMessage) {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(payload));\n }\n }\n\n close() {\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://localhost:8080\";\n","import CryptoService from \"../utilities/crypto\";\nimport QRCode from \"qrcode\";\nimport {\n PelicanAuthConfig,\n PelicanAuthEventMap,\n ISocketMessage,\n IdentityResult,\n} from \"../types/types\";\nimport {\n storeAuthSession,\n getAuthSession,\n clearAuthSession,\n AuthSession,\n} from \"../utilities/storage\";\nimport { StateMachine } from \"../utilities/stateMachine\";\nimport { Transport } from \"../utilities/transport\";\n\nimport { BASEURL } from \"../constants\";\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * PelicanAuth SDK\n * Handles cross-device authentication via WebSockets and E2EE (End-to-End Encryption).\n */\nexport class PelicanAuthentication {\n private readonly crypto = new CryptoService();\n private readonly stateMachine = new StateMachine();\n\n private transport?: Transport;\n private sessionId = \"\";\n private sessionKey: string | null = null;\n\n private visibilityHandler?: () => void;\n\n private listeners: {\n [K in keyof PelicanAuthEventMap]?: Set<Listener<any>>;\n } = {};\n\n private readonly config: Required<PelicanAuthConfig>;\n\n constructor(config: PelicanAuthConfig) {\n if (!config.publicKey) throw new Error(\"Missing publicKey\");\n if (!config.projectId) throw new Error(\"Missing projectId\");\n if (!config.authType) throw new Error(\"Missing authType\");\n\n this.config = {\n continuousMode: false,\n forceQRCode: false,\n ...config,\n };\n\n // Sync internal state machine changes with the 'state' event\n this.stateMachine.subscribe((s) => this.emit(\"state\", s));\n this.attachVisibilityRecovery();\n }\n\n /* -------------------- public API -------------------- */\n\n /**\n * Subscribe to SDK events (qr, deeplink, success, error, state)\n * @returns Unsubscribe function\n */\n on<K extends keyof PelicanAuthEventMap>(\n event: K,\n cb: Listener<PelicanAuthEventMap[K]>\n ) {\n this.listeners[event] ??= new Set();\n this.listeners[event]!.add(cb);\n return () => this.listeners[event]!.delete(cb);\n }\n\n /**\n * Initializes the authentication flow.\n * Fetches a relay, establishes a WebSocket, and generates the E2EE session key.\n */\n async start() {\n if (this.stateMachine.current !== \"idle\") return;\n\n this.resetSession();\n this.stateMachine.transition(\"initializing\");\n\n try {\n const relay = await this.fetchRelayUrl();\n\n // Generate a fresh symmetric key for this specific session\n this.sessionKey = this.crypto.generateSymmetricKey();\n this.sessionId = crypto.randomUUID() + crypto.randomUUID();\n\n this.transport = new Transport({\n onOpen: () => {\n // Register this browser session with the relay\n this.transport!.send({\n type: \"register\",\n sessionID: this.sessionId,\n ...this.config,\n });\n this.stateMachine.transition(\"awaiting-pair\");\n },\n onMessage: (msg: ISocketMessage) => this.handleMessage(msg),\n onError: () => this.fail(new Error(\"WebSocket connection failed\")),\n });\n\n this.transport.connect(relay);\n await this.emitEntryPoint();\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(\"Start failed\"));\n }\n }\n\n /**\n * Manually stops the authentication process and closes connections.\n */\n stop() {\n this.terminate(false);\n }\n\n /* -------------------- internals -------------------- */\n\n /** Fetches the WebSocket Relay URL from the backend */\n private async fetchRelayUrl(): Promise<string> {\n const { publicKey, projectId, authType } = this.config;\n const res = await fetch(\n `${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`\n );\n\n if (!res.ok) {\n throw new Error(\"Failed to fetch relay URL\");\n }\n\n const json = await res.json();\n return json.relay_url;\n }\n\n /**\n * Decides whether to show a QR code (Desktop) or a Deep Link (Mobile).\n */\n private async emitEntryPoint() {\n const payload = {\n sessionID: this.sessionId,\n sessionKey: this.sessionKey,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n projectId: this.config.projectId,\n url: window.location.href,\n };\n\n const shouldUseQR =\n this.config.forceQRCode ||\n !/Android|iPhone|iPad/i.test(navigator.userAgent);\n\n if (!shouldUseQR && this.sessionKey) {\n // For mobile: Store session locally so we can recover when the user returns from the app\n storeAuthSession(this.sessionId, this.sessionKey, 5 * 60_000);\n this.emit(\n \"deeplink\",\n `pelicanvault://auth/deep-link?data=${encodeURIComponent(\n JSON.stringify(payload)\n )}`\n );\n } else {\n // For desktop: Generate a QR code for the mobile app to scan\n const qr = await QRCode.toDataURL(JSON.stringify(payload));\n this.emit(\"qr\", qr);\n }\n }\n\n /** Main WebSocket message router */\n private handleMessage(msg: ISocketMessage) {\n switch (msg.type) {\n case \"paired\":\n // The mobile device has scanned the QR/opened the link\n this.stateMachine.transition(\"paired\");\n this.transport!.send({\n type: \"authenticate\",\n sessionID: this.sessionId,\n ...this.config,\n url: window.location.href,\n });\n return;\n\n case \"phone-auth-success\":\n // Mobile device successfully authenticated and sent encrypted credentials\n this.handleAuthSuccess(msg);\n return;\n\n case \"phone-terminated\":\n this.fail(new Error(\"Authenticating device terminated the connection\"));\n this.restartIfContinuous();\n return;\n\n case \"confirmed\":\n // Handshake complete\n this.terminate(true);\n this.restartIfContinuous();\n return;\n }\n }\n\n /**\n * Decrypts the identity payload received from the phone using the session key.\n */\n private handleAuthSuccess(msg: ISocketMessage) {\n if (!this.sessionKey || !msg.cipher || !msg.nonce) {\n this.fail(new Error(\"Invalid authentication payload\"));\n this.restartIfContinuous();\n return;\n }\n\n try {\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: msg.cipher, nonce: msg.nonce },\n keyString: this.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid authentication data\"));\n this.restartIfContinuous();\n return;\n }\n\n const result: IdentityResult = JSON.parse(decrypted);\n\n this.emit(\"success\", result);\n this.stateMachine.transition(\"authenticated\");\n\n // Signal to the relay/phone that we have received and decrypted the data\n this.transport?.send({\n type: \"confirm\",\n sessionID: this.sessionId,\n });\n } catch {\n this.fail(new Error(\"Failed to decrypt authentication data\"));\n this.restartIfContinuous();\n }\n }\n\n /**\n * Logic to handle users returning to the browser tab after using the mobile app.\n * Checks the server for a completed session that might have finished while in background.\n */\n\n private async getCachedEntry(cached: AuthSession) {\n try {\n const res = await fetch(\n `${BASEURL}/session?session_id=${cached.sessionId}`\n );\n\n if (!res.ok) throw new Error(\"Invalid session\");\n\n const data = await res.json();\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: data.cipher, nonce: data.nonce },\n keyString: cached.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid session data\"));\n this.restartIfContinuous();\n return;\n }\n const result: IdentityResult = JSON.parse(decrypted);\n this.emit(\"success\", result);\n clearAuthSession();\n } catch {\n this.fail(new Error(\"Session recovery failed\"));\n this.restartIfContinuous();\n }\n }\n\n private attachVisibilityRecovery() {\n this.visibilityHandler = async () => {\n if (document.visibilityState !== \"visible\") return;\n\n const cached = getAuthSession();\n if (!cached) return;\n await this.getCachedEntry(cached);\n };\n\n document.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n private detachVisibilityRecovery() {\n if (this.visibilityHandler) {\n document.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = undefined;\n }\n }\n\n /** Cleans up the current session state */\n private terminate(success: boolean) {\n if (!this.transport) return;\n if (!success) {\n this.transport?.send({\n type: \"client-terminated\",\n sessionID: this.sessionId,\n projectId: this.config.projectId,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n });\n }\n\n setTimeout(() => {\n this.transport?.close();\n }, 150);\n\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"terminated\");\n }\n\n private restartIfContinuous() {\n if (!this.config.continuousMode) return;\n this.terminate(false);\n this.stateMachine.transition(\"idle\");\n\n setTimeout(() => {\n this.start();\n }, 150);\n }\n\n private resetSession() {\n this.sessionId = \"\";\n this.sessionKey = null;\n }\n\n /** Completely destroys the instance and removes all listeners */\n destroy() {\n this.detachVisibilityRecovery();\n this.transport?.close();\n this.listeners = {};\n }\n\n private emit<K extends keyof PelicanAuthEventMap>(\n event: K,\n payload: PelicanAuthEventMap[K]\n ) {\n this.listeners[event]?.forEach((cb) => cb(payload));\n }\n\n private fail(err: Error) {\n this.emit(\"error\", err);\n this.stateMachine.transition(\"error\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/utilities/crypto.ts","../src/utilities/storage.ts","../src/utilities/stateMachine.ts","../src/utilities/transport.ts","../src/constants.ts","../src/engine/engine.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import nacl from \"tweetnacl\";\nimport { decodeBase64, encodeBase64 } from \"tweetnacl-util\";\n\ninterface EncryptedMessage {\n cipher: string; // Base64 encoded\n nonce: string; // Base64 encoded\n}\n\nexport class CryptoService {\n generateSymmetricKey(): string {\n const key = nacl.randomBytes(32);\n return encodeBase64(key);\n }\n\n /**\n * Encrypt with symmetric key (secret-key encryption)\n * Use this when both parties share the same secret key\n * @param plaintext - Message to encrypt\n * @param keyString - Symmetric key (base64)\n * @returns Encrypted message with nonce\n */\n encryptSymmetric({\n plaintext,\n keyString,\n }: {\n plaintext: string;\n keyString: string;\n }): EncryptedMessage {\n const key = decodeBase64(keyString);\n const nonce = nacl.randomBytes(24);\n\n const messageBytes = new TextEncoder().encode(plaintext);\n\n const ciphertext = nacl.secretbox(messageBytes, nonce, key);\n\n return {\n cipher: encodeBase64(ciphertext),\n nonce: encodeBase64(nonce),\n };\n }\n\n /**\n * Decrypt with symmetric key\n * @param encrypted - Encrypted message with nonce\n * @param keyString - Symmetric key (base64)\n * @returns Decrypted plaintext\n */\n decryptSymmetric({\n encrypted,\n keyString,\n }: {\n encrypted: EncryptedMessage;\n keyString: string;\n }): string | null {\n try {\n const key = decodeBase64(keyString);\n const ciphertextBytes = decodeBase64(encrypted.cipher);\n const nonceBytes = decodeBase64(encrypted.nonce);\n\n const decrypted = nacl.secretbox.open(ciphertextBytes, nonceBytes, key);\n\n if (!decrypted) {\n throw new Error(\"Decryption failed - invalid key or corrupted data\");\n }\n\n const decoded = new TextDecoder().decode(decrypted);\n return decoded;\n } catch (error) {\n console.error(\"Decryption failed\", error);\n return null;\n }\n }\n}\n\nexport default CryptoService;\nexport type { EncryptedMessage };\n","// hybridStorage.ts\n/**\n * Hybrid storage: Try sessionStorage first, fallback to memory\n * Best of both worlds - survives page refresh but auto-cleans\n */\n\ninterface StorageOptions {\n ttlMs?: number;\n useSessionStorage?: boolean;\n}\n\nclass AuthStorage {\n private readonly prefix = \"pelican_auth_\";\n private readonly defaultTTL = 5 * 60 * 1000; // 5 minutes\n private memoryCache: Map<string, { value: any; expiresAt: number }> =\n new Map();\n\n /**\n * Store auth session with automatic cleanup\n */\n set(key: string, value: any, options: StorageOptions = {}): void {\n const { ttlMs = this.defaultTTL, useSessionStorage = true } = options;\n\n const expiresAt = Date.now() + ttlMs;\n const data = { value, expiresAt };\n\n // Try sessionStorage first\n if (useSessionStorage) {\n try {\n sessionStorage.setItem(`${this.prefix}${key}`, JSON.stringify(data));\n } catch (error) {\n console.warn(\"SessionStorage unavailable, using memory:\", error);\n this.setMemory(key, data);\n }\n } else {\n this.setMemory(key, data);\n }\n }\n\n /**\n * Get stored value if not expired\n */\n get(key: string): any | null {\n // Try sessionStorage first\n try {\n const stored = sessionStorage.getItem(`${this.prefix}${key}`);\n if (stored) {\n const data = JSON.parse(stored);\n\n // Check expiration\n if (Date.now() > data.expiresAt) {\n this.remove(key);\n return null;\n }\n\n return data.value;\n }\n } catch (error) {\n // Fallback to memory\n }\n\n // Try memory cache\n return this.getMemory(key);\n }\n\n /**\n * Remove specific key\n */\n remove(key: string): void {\n try {\n sessionStorage.removeItem(`${this.prefix}${key}`);\n } catch (error) {\n // Ignore\n }\n this.memoryCache.delete(key);\n }\n\n /**\n * Clear all auth data\n */\n clear(): void {\n // Clear sessionStorage\n try {\n Object.keys(sessionStorage).forEach((key) => {\n if (key.startsWith(this.prefix)) {\n sessionStorage.removeItem(key);\n }\n });\n } catch (error) {\n // Ignore\n }\n\n // Clear memory\n this.memoryCache.clear();\n }\n\n // ========================================\n // Memory cache helpers\n // ========================================\n\n private setMemory(\n key: string,\n data: { value: any; expiresAt: number }\n ): void {\n this.memoryCache.set(key, data);\n\n // Schedule cleanup\n const ttl = data.expiresAt - Date.now();\n setTimeout(() => {\n this.memoryCache.delete(key);\n }, ttl);\n }\n\n private getMemory(key: string): any | null {\n const data = this.memoryCache.get(key);\n if (!data) return null;\n\n if (Date.now() > data.expiresAt) {\n this.memoryCache.delete(key);\n return null;\n }\n\n return data.value;\n }\n}\n\n// Singleton instance\nconst storage = new AuthStorage();\n\n// ========================================\n// Convenience functions for auth flow\n// ========================================\n\nexport interface AuthSession {\n sessionId: string;\n sessionKey: string;\n}\n\nexport const storeAuthSession = (\n sessionId: string,\n sessionKey: string,\n ttlMs?: number\n): void => {\n storage.set(\"session\", { sessionId, sessionKey }, { ttlMs });\n};\n\nexport const getAuthSession = (): AuthSession | null => {\n return storage.get(\"session\");\n};\n\nexport const clearAuthSession = (): void => {\n storage.remove(\"session\");\n};\n\nexport const clearAllAuthData = (): void => {\n storage.clear();\n};\n\nexport default storage;\n","import type { PelicanAuthState } from \"../types/types\";\n\nexport class StateMachine {\n private state: PelicanAuthState = \"idle\";\n private listeners = new Set<(s: PelicanAuthState) => void>();\n\n get current() {\n return this.state;\n }\n\n transition(next: PelicanAuthState) {\n this.state = next;\n this.listeners.forEach((l) => l(next));\n }\n\n subscribe(fn: (s: PelicanAuthState) => void) {\n this.listeners.add(fn);\n return () => this.listeners.delete(fn);\n }\n}\n","import { ISocketMessage } from \"../types/types\";\n\ntype TransportHandlers = {\n onOpen?: () => void;\n onMessage?: (msg: ISocketMessage) => void;\n onError?: (err: Event) => void;\n onClose?: (ev: CloseEvent) => void;\n};\n\ninterface PelicanMessageEvent extends MessageEvent {\n data: ISocketMessage;\n}\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => this.handlers.onOpen?.();\n this.socket.onmessage = (e: PelicanMessageEvent) =>\n this.handlers.onMessage?.(e.data);\n this.socket.onerror = (e: Event) => this.handlers.onError?.(e);\n this.socket.onclose = (e: CloseEvent) => this.handlers.onClose?.(e);\n }\n\n send(payload: ISocketMessage) {\n if (this.socket?.readyState === WebSocket.OPEN) {\n this.socket.send(JSON.stringify(payload));\n }\n }\n\n close() {\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://localhost:8080\";\n","import CryptoService from \"../utilities/crypto\";\nimport QRCode from \"qrcode\";\nimport {\n PelicanAuthConfig,\n PelicanAuthEventMap,\n ISocketMessage,\n IdentityResult,\n} from \"../types/types\";\nimport {\n storeAuthSession,\n getAuthSession,\n clearAuthSession,\n AuthSession,\n} from \"../utilities/storage\";\nimport { StateMachine } from \"../utilities/stateMachine\";\nimport { Transport } from \"../utilities/transport\";\n\nimport { BASEURL } from \"../constants\";\n\ntype Listener<T> = (payload: T) => void;\n\n/**\n * PelicanAuth SDK\n * Handles cross-device authentication via WebSockets and E2EE (End-to-End Encryption).\n */\nexport class PelicanAuthentication {\n private readonly crypto = new CryptoService();\n private readonly stateMachine = new StateMachine();\n\n private transport?: Transport;\n private sessionId = \"\";\n private sessionKey: string | null = null;\n\n private visibilityHandler?: () => void;\n\n private listeners: {\n [K in keyof PelicanAuthEventMap]?: Set<Listener<any>>;\n } = {};\n\n private readonly config: Required<PelicanAuthConfig>;\n\n constructor(config: PelicanAuthConfig) {\n if (!config.publicKey) throw new Error(\"Missing publicKey\");\n if (!config.projectId) throw new Error(\"Missing projectId\");\n if (!config.authType) throw new Error(\"Missing authType\");\n\n this.config = {\n continuousMode: false,\n forceQRCode: false,\n ...config,\n };\n\n // Sync internal state machine changes with the 'state' event\n this.stateMachine.subscribe((s) => this.emit(\"state\", s));\n this.attachVisibilityRecovery();\n }\n\n /* -------------------- public API -------------------- */\n\n /**\n * Subscribe to SDK events (qr, deeplink, success, error, state)\n * @returns Unsubscribe function\n */\n on<K extends keyof PelicanAuthEventMap>(\n event: K,\n cb: Listener<PelicanAuthEventMap[K]>\n ) {\n this.listeners[event] ??= new Set();\n this.listeners[event]!.add(cb);\n return () => this.listeners[event]!.delete(cb);\n }\n\n /**\n * Initializes the authentication flow.\n * Fetches a relay, establishes a WebSocket, and generates the E2EE session key.\n */\n async start() {\n if (this.stateMachine.current !== \"idle\") return;\n\n this.resetSession();\n this.stateMachine.transition(\"initializing\");\n\n try {\n const relay = await this.fetchRelayUrl();\n\n // Generate a fresh symmetric key for this specific session\n this.sessionKey = this.crypto.generateSymmetricKey();\n this.sessionId = crypto.randomUUID() + crypto.randomUUID();\n\n this.transport = new Transport({\n onOpen: () => {\n // Register this browser session with the relay\n this.transport!.send({\n type: \"register\",\n sessionID: this.sessionId,\n ...this.config,\n });\n this.stateMachine.transition(\"awaiting-pair\");\n },\n onMessage: (msg: ISocketMessage) => this.handleMessage(msg),\n onError: () => this.fail(new Error(\"WebSocket connection failed\")),\n });\n\n this.transport.connect(relay);\n await this.emitEntryPoint();\n } catch (err) {\n this.fail(err instanceof Error ? err : new Error(\"Start failed\"));\n }\n }\n\n /**\n * Manually stops the authentication process and closes connections.\n */\n stop() {\n this.terminate(false);\n }\n\n /* -------------------- internals -------------------- */\n\n /** Fetches the WebSocket Relay URL from the backend */\n private async fetchRelayUrl(): Promise<string> {\n const { publicKey, projectId, authType } = this.config;\n const res = await fetch(\n `${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`\n );\n\n if (!res.ok) {\n throw new Error(\"Failed to fetch relay URL\");\n }\n\n const json = await res.json();\n return json.relay_url;\n }\n\n /**\n * Decides whether to show a QR code (Desktop) or a Deep Link (Mobile).\n */\n private async emitEntryPoint() {\n const payload = {\n sessionID: this.sessionId,\n sessionKey: this.sessionKey,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n projectId: this.config.projectId,\n url: window.location.href,\n };\n\n const shouldUseQR =\n this.config.forceQRCode ||\n !/Android|iPhone|iPad/i.test(navigator.userAgent);\n\n if (!shouldUseQR && this.sessionKey) {\n // For mobile: Store session locally so we can recover when the user returns from the app\n storeAuthSession(this.sessionId, this.sessionKey, 5 * 60_000);\n this.emit(\n \"deeplink\",\n `pelicanvault://auth/deep-link?data=${encodeURIComponent(\n JSON.stringify(payload)\n )}`\n );\n } else {\n // For desktop: Generate a QR code for the mobile app to scan\n const qr = await QRCode.toDataURL(JSON.stringify(payload));\n this.emit(\"qr\", qr);\n }\n }\n\n /** Main WebSocket message router */\n private handleMessage(msg: ISocketMessage) {\n console.log(msg);\n\n switch (msg.type) {\n case \"paired\":\n console.log(\"Paired\");\n\n this.stateMachine.transition(\"paired\");\n this.transport!.send({\n type: \"authenticate\",\n sessionID: this.sessionId,\n ...this.config,\n url: window.location.href,\n });\n return;\n\n case \"phone-auth-success\":\n // Mobile device successfully authenticated and sent encrypted credentials\n this.handleAuthSuccess(msg);\n return;\n\n case \"phone-terminated\":\n this.fail(new Error(\"Authenticating device terminated the connection\"));\n this.restartIfContinuous();\n return;\n\n case \"confirmed\":\n // Handshake complete\n this.terminate(true);\n this.restartIfContinuous();\n return;\n }\n }\n\n /**\n * Decrypts the identity payload received from the phone using the session key.\n */\n private handleAuthSuccess(msg: ISocketMessage) {\n if (!this.sessionKey || !msg.cipher || !msg.nonce) {\n this.fail(new Error(\"Invalid authentication payload\"));\n this.restartIfContinuous();\n return;\n }\n\n try {\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: msg.cipher, nonce: msg.nonce },\n keyString: this.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid authentication data\"));\n this.restartIfContinuous();\n return;\n }\n\n const result: IdentityResult = JSON.parse(decrypted);\n\n this.emit(\"success\", result);\n this.stateMachine.transition(\"authenticated\");\n\n // Signal to the relay/phone that we have received and decrypted the data\n this.transport?.send({\n type: \"confirm\",\n sessionID: this.sessionId,\n });\n } catch {\n this.fail(new Error(\"Failed to decrypt authentication data\"));\n this.restartIfContinuous();\n }\n }\n\n /**\n * Logic to handle users returning to the browser tab after using the mobile app.\n * Checks the server for a completed session that might have finished while in background.\n */\n\n private async getCachedEntry(cached: AuthSession) {\n try {\n const res = await fetch(\n `${BASEURL}/session?session_id=${cached.sessionId}`\n );\n\n if (!res.ok) throw new Error(\"Invalid session\");\n\n const data = await res.json();\n const decrypted = this.crypto.decryptSymmetric({\n encrypted: { cipher: data.cipher, nonce: data.nonce },\n keyString: cached.sessionKey,\n });\n\n if (!decrypted) {\n this.fail(new Error(\"Invalid session data\"));\n this.restartIfContinuous();\n return;\n }\n const result: IdentityResult = JSON.parse(decrypted);\n this.emit(\"success\", result);\n clearAuthSession();\n } catch {\n this.fail(new Error(\"Session recovery failed\"));\n this.restartIfContinuous();\n }\n }\n\n private attachVisibilityRecovery() {\n this.visibilityHandler = async () => {\n if (document.visibilityState !== \"visible\") return;\n\n const cached = getAuthSession();\n if (!cached) return;\n await this.getCachedEntry(cached);\n };\n\n document.addEventListener(\"visibilitychange\", this.visibilityHandler);\n }\n\n private detachVisibilityRecovery() {\n if (this.visibilityHandler) {\n document.removeEventListener(\"visibilitychange\", this.visibilityHandler);\n this.visibilityHandler = undefined;\n }\n }\n\n /** Cleans up the current session state */\n private terminate(success: boolean) {\n if (!this.transport) return;\n if (!success) {\n this.transport?.send({\n type: \"client-terminated\",\n sessionID: this.sessionId,\n projectId: this.config.projectId,\n publicKey: this.config.publicKey,\n authType: this.config.authType,\n });\n }\n\n setTimeout(() => {\n this.transport?.close();\n }, 150);\n\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"terminated\");\n }\n\n private restartIfContinuous() {\n if (!this.config.continuousMode) return;\n this.terminate(false);\n this.stateMachine.transition(\"idle\");\n\n setTimeout(() => {\n this.start();\n }, 150);\n }\n\n private resetSession() {\n this.sessionId = \"\";\n this.sessionKey = null;\n }\n\n /** Completely destroys the instance and removes all listeners */\n destroy() {\n this.detachVisibilityRecovery();\n this.transport?.close();\n this.listeners = {};\n }\n\n private emit<K extends keyof PelicanAuthEventMap>(\n event: K,\n payload: PelicanAuthEventMap[K]\n ) {\n this.listeners[event]?.forEach((cb) => cb(payload));\n }\n\n private fail(err: Error) {\n this.emit(\"error\", err);\n this.stateMachine.transition(\"error\");\n }\n}\n"]}
|