@pelican-identity/auth-core 1.2.6 → 1.2.8
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 +106 -502
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,570 +1,174 @@
|
|
|
1
|
-
|
|
1
|
+
This documentation provides a comprehensive guide for using the Pelican Identity Vanilla SDK. It covers three integration paths: using the pre-built UI via CDN, using the UI wrapper via NPM, and building a custom UI using the Core Engine.
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Pelican Identity Vanilla SDK
|
|
6
6
|
|
|
7
|
-
|
|
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
|
|
7
|
+
The Vanilla SDK provides a framework-agnostic way to integrate Pelican authentication. It includes a high-level UI wrapper that mirrors the React experience and a low-level Core Engine for custom implementations.
|
|
14
8
|
|
|
15
9
|
## Installation
|
|
16
10
|
|
|
17
|
-
|
|
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
|
|
11
|
+
### 1. Via CDN (Easiest)
|
|
74
12
|
|
|
75
|
-
|
|
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 |
|
|
13
|
+
Ideal for projects without a build step. Includes the UI and logic in a single file.
|
|
82
14
|
|
|
83
|
-
|
|
15
|
+
```html
|
|
16
|
+
<link
|
|
17
|
+
rel="stylesheet"
|
|
18
|
+
href="https://cdn.jsdelivr.net/npm/@pelican-identity/vanilla@1.0.2/dist/pelican.css"
|
|
19
|
+
/>
|
|
84
20
|
|
|
85
|
-
|
|
21
|
+
<script src="https://cdn.jsdelivr.net/npm/@pelican-identity/vanilla@1.0.2/dist/index.min.js"></script>
|
|
86
22
|
|
|
87
|
-
|
|
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
|
-
```
|
|
23
|
+
<div id="pelican-auth-root"></div>
|
|
100
24
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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();
|
|
25
|
+
<script>
|
|
26
|
+
const auth = Pelican.createPelicanAuth("pelican-auth-root", {
|
|
27
|
+
publicKey: "your-business-public-key",
|
|
28
|
+
projectId: "your-project-id",
|
|
29
|
+
authType: "login",
|
|
30
|
+
onSuccess: (data) => console.log("Success:", data),
|
|
31
|
+
onError: (err) => console.error("Error:", err),
|
|
32
|
+
});
|
|
33
|
+
</script>
|
|
120
34
|
```
|
|
121
35
|
|
|
122
|
-
###
|
|
36
|
+
### 2. Via NPM/PNPM
|
|
123
37
|
|
|
124
|
-
|
|
38
|
+
Ideal for modern web apps (Vue, Svelte, or Vanilla TS) using a bundler like Vite or Webpack.
|
|
125
39
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
Emitted when the authentication state changes.
|
|
40
|
+
```bash
|
|
41
|
+
npm install @pelican-identity/vanilla
|
|
129
42
|
|
|
130
|
-
```typescript
|
|
131
|
-
auth.on("state", (state) => {
|
|
132
|
-
console.log("Current state:", state);
|
|
133
|
-
});
|
|
134
43
|
```
|
|
135
44
|
|
|
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
45
|
```typescript
|
|
152
|
-
|
|
153
|
-
|
|
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);
|
|
46
|
+
import { createPelicanAuth } from "@pelican-identity/vanilla";
|
|
47
|
+
import "@pelican-identity/vanilla/dist/pelican.css";
|
|
157
48
|
|
|
158
|
-
|
|
49
|
+
const cleanup = createPelicanAuth("container-id", {
|
|
50
|
+
publicKey: "...",
|
|
51
|
+
projectId: "...",
|
|
52
|
+
authType: "login",
|
|
53
|
+
onSuccess: (identity) => {
|
|
54
|
+
console.log(identity.user_id);
|
|
55
|
+
},
|
|
159
56
|
});
|
|
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
57
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
console.error("Error:", error.message);
|
|
180
|
-
});
|
|
58
|
+
// To stop and cleanup the instance
|
|
59
|
+
// cleanup();
|
|
181
60
|
```
|
|
182
61
|
|
|
183
|
-
|
|
62
|
+
---
|
|
184
63
|
|
|
185
|
-
|
|
64
|
+
## UI Wrapper API Reference (`createPelicanAuth`)
|
|
186
65
|
|
|
187
|
-
|
|
188
|
-
auth.on("qr", (dataUrl: string) => {
|
|
189
|
-
// Display in an <img> tag
|
|
190
|
-
document.getElementById("qr").src = dataUrl;
|
|
191
|
-
});
|
|
192
|
-
```
|
|
66
|
+
The `createPelicanAuth` function initializes the Pelican UI inside a target DOM element.
|
|
193
67
|
|
|
194
|
-
|
|
68
|
+
| Option | Type | Required | Description |
|
|
69
|
+
| ---------------- | ---------- | -------- | --------------------------------------------- |
|
|
70
|
+
| `publicKey` | `string` | ✅ | Business public key from Pelican dashboard |
|
|
71
|
+
| `projectId` | `string` | ✅ | Project ID from Pelican dashboard |
|
|
72
|
+
| `authType` | `AuthType` | ✅ | `"signup"`, `"login"`, or `"id-verification"` |
|
|
73
|
+
| `onSuccess` | `Function` | ✅ | Callback with `IdentityResult` |
|
|
74
|
+
| `onError` | `Function` | ❌ | Callback for errors |
|
|
75
|
+
| `onClose` | `Function` | ❌ | Callback when the user closes the modal |
|
|
76
|
+
| `continuousMode` | `boolean` | ❌ | Auto-restart auth after completion |
|
|
77
|
+
| `forceQRCode` | `boolean` | ❌ | Always show QR even on mobile |
|
|
78
|
+
| `buttonText` | `string` | ❌ | Custom text for the Pelican button |
|
|
195
79
|
|
|
196
|
-
|
|
80
|
+
---
|
|
197
81
|
|
|
198
|
-
|
|
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
|
-
```
|
|
82
|
+
## Low-Level Core Usage (Custom UI)
|
|
206
83
|
|
|
207
|
-
|
|
84
|
+
If you want to build your own UI from scratch, use the `@pelican-identity/auth-core` package directly. This provides the event-driven state machine without any HTML/CSS.
|
|
208
85
|
|
|
209
|
-
###
|
|
86
|
+
### Basic Implementation
|
|
210
87
|
|
|
211
88
|
```typescript
|
|
212
|
-
import {
|
|
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);
|
|
89
|
+
import { PelicanAuthentication } from "@pelican-identity/auth-core";
|
|
228
90
|
|
|
229
|
-
|
|
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({
|
|
91
|
+
const pelican = new PelicanAuthentication({
|
|
319
92
|
publicKey: "your-key",
|
|
320
|
-
projectId: "your-
|
|
93
|
+
projectId: "your-id",
|
|
321
94
|
authType: "login",
|
|
322
95
|
});
|
|
323
96
|
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
<img src="${dataUrl}" alt="Scan to login" />
|
|
328
|
-
<p>Scan with Pelican Vault app</p>
|
|
329
|
-
`;
|
|
97
|
+
// 1. Listen for the QR code
|
|
98
|
+
pelican.on("qr", (qrDataUrl) => {
|
|
99
|
+
document.getElementById("my-qr").src = qrDataUrl;
|
|
330
100
|
});
|
|
331
101
|
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
<a href="${url}" class="btn">Open Pelican App</a>
|
|
336
|
-
`;
|
|
102
|
+
// 2. Listen for Mobile Deep Links
|
|
103
|
+
pelican.on("deeplink", (url) => {
|
|
104
|
+
document.getElementById("mobile-link").href = url;
|
|
337
105
|
});
|
|
338
106
|
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
console.log("
|
|
342
|
-
// Redirect or update UI
|
|
343
|
-
window.location.href = "/dashboard";
|
|
107
|
+
// 3. Track State Changes
|
|
108
|
+
pelican.on("state", (state) => {
|
|
109
|
+
console.log("Current state:", state); // 'initializing', 'paired', etc.
|
|
344
110
|
});
|
|
345
111
|
|
|
346
|
-
// Handle
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
<p class="error">${error.message}</p>
|
|
350
|
-
<button onclick="auth.start()">Retry</button>
|
|
351
|
-
`;
|
|
112
|
+
// 4. Handle Completion
|
|
113
|
+
pelican.on("success", (identity) => {
|
|
114
|
+
alert(`Welcome ${identity.user_id}`);
|
|
352
115
|
});
|
|
353
116
|
|
|
354
|
-
// Start
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
// Cleanup on page unload
|
|
358
|
-
window.addEventListener("beforeunload", () => {
|
|
359
|
-
auth.destroy();
|
|
360
|
-
});
|
|
117
|
+
// Start the engine
|
|
118
|
+
pelican.start();
|
|
361
119
|
```
|
|
362
120
|
|
|
363
|
-
|
|
121
|
+
---
|
|
364
122
|
|
|
365
|
-
|
|
123
|
+
## Authentication Flow logic
|
|
366
124
|
|
|
367
|
-
|
|
125
|
+
The SDK manages the transition between Desktop and Mobile automatically:
|
|
368
126
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
authType: "login",
|
|
374
|
-
continuousMode: true, // Enable continuous mode
|
|
375
|
-
});
|
|
127
|
+
1. **Desktop:** Emits a `qr` event containing a Base64 Data URL.
|
|
128
|
+
2. **Mobile:** Emits a `deeplink` event. Browsers should provide a "Open App" button using this URL.
|
|
129
|
+
3. **Paired:** Once the user scans/clicks, the state moves to `paired`.
|
|
130
|
+
4. **Success:** After biometric/PIN approval in the Pelican app, the `success` event fires with user data.
|
|
376
131
|
|
|
377
|
-
|
|
378
|
-
// Process user
|
|
379
|
-
console.log("User authenticated:", identity);
|
|
380
|
-
// Session will automatically restart after ~300ms
|
|
381
|
-
});
|
|
132
|
+
---
|
|
382
133
|
|
|
383
|
-
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
### Force QR Code on Mobile
|
|
134
|
+
## Identity Result Structure
|
|
387
135
|
|
|
388
|
-
|
|
136
|
+
Regardless of which integration path you choose, the successful authentication returns this object:
|
|
389
137
|
|
|
390
138
|
```typescript
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
});
|
|
139
|
+
interface IdentityResult {
|
|
140
|
+
user_id: string; // Unique business-scoped identifier
|
|
141
|
+
user_data?: {
|
|
142
|
+
first_name?: string;
|
|
143
|
+
email?: { value: string; verified: boolean };
|
|
144
|
+
phone?: { number: string; verified: boolean };
|
|
145
|
+
};
|
|
146
|
+
id_verification?: {
|
|
147
|
+
status: "Approved" | "Declined";
|
|
148
|
+
document_type: string;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
465
151
|
```
|
|
466
152
|
|
|
467
|
-
|
|
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
|
|
153
|
+
---
|
|
488
154
|
|
|
489
155
|
## Troubleshooting
|
|
490
156
|
|
|
491
|
-
###
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
auth.on("qr", (dataUrl) => {
|
|
495
|
-
if (!dataUrl) {
|
|
496
|
-
console.error("QR code generation failed");
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
```
|
|
157
|
+
### `crypto.randomUUID is not a function`
|
|
500
158
|
|
|
501
|
-
|
|
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
|
-
```
|
|
159
|
+
**Cause:** Browsers only allow the Crypto API in **Secure Contexts** (HTTPS or localhost).
|
|
160
|
+
**Fix:** Ensure you are serving your site over HTTPS or using `http://localhost`.
|
|
557
161
|
|
|
558
|
-
|
|
162
|
+
### QR Code not showing
|
|
559
163
|
|
|
560
|
-
|
|
164
|
+
**Cause:** The engine might be in `initializing` state or the domain isn't whitelisted.
|
|
165
|
+
**Fix:** Check your Pelican Dashboard and ensure your current domain (including port if applicable) is added to the project whitelist.
|
|
561
166
|
|
|
562
|
-
|
|
167
|
+
### Styles not applying
|
|
563
168
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
- Email: support@pelicanidentity.com
|
|
169
|
+
**Cause:** The CSS file is missing.
|
|
170
|
+
**Fix:** If using NPM, ensure you import `@pelican-identity/vanilla/dist/pelican.css`. If using the CDN `.min.js` version with `injectStyle: true`, this should be automatic.
|
|
567
171
|
|
|
568
|
-
|
|
172
|
+
---
|
|
569
173
|
|
|
570
|
-
|
|
174
|
+
**Would you like me to help you create a "Getting Started" guide specifically for the Pelican Dashboard setup to complement this SDK documentation?**
|
package/dist/index.d.mts
CHANGED
|
@@ -182,6 +182,6 @@ declare const getAuthSession: () => AuthSession | null;
|
|
|
182
182
|
declare const clearAuthSession: () => void;
|
|
183
183
|
declare const clearAllAuthData: () => void;
|
|
184
184
|
|
|
185
|
-
declare const BASEURL = "
|
|
185
|
+
declare const BASEURL = "https://identityapi.pelicanidentity.com";
|
|
186
186
|
|
|
187
187
|
export { type AuthSession, type AuthType, BASEURL, CryptoService, type IEmail, type IKycData, type IPhone, type ISocketMessage, type IUserData, type IdCardTypes, type IdentityResult, type PelicanAuthConfig, type PelicanAuthEventMap, type PelicanAuthState, PelicanAuthentication, type PelicanWebAuthProps, StateMachine, Transport, clearAllAuthData, clearAuthSession, getAuthSession, storeAuthSession };
|
package/dist/index.d.ts
CHANGED
|
@@ -182,6 +182,6 @@ declare const getAuthSession: () => AuthSession | null;
|
|
|
182
182
|
declare const clearAuthSession: () => void;
|
|
183
183
|
declare const clearAllAuthData: () => void;
|
|
184
184
|
|
|
185
|
-
declare const BASEURL = "
|
|
185
|
+
declare const BASEURL = "https://identityapi.pelicanidentity.com";
|
|
186
186
|
|
|
187
187
|
export { type AuthSession, type AuthType, BASEURL, CryptoService, type IEmail, type IKycData, type IPhone, type ISocketMessage, type IUserData, type IdCardTypes, type IdentityResult, type PelicanAuthConfig, type PelicanAuthEventMap, type PelicanAuthState, PelicanAuthentication, type PelicanWebAuthProps, StateMachine, Transport, clearAllAuthData, clearAuthSession, getAuthSession, storeAuthSession };
|
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ var Transport = class {
|
|
|
241
241
|
};
|
|
242
242
|
|
|
243
243
|
// src/constants.ts
|
|
244
|
-
var BASEURL = "
|
|
244
|
+
var BASEURL = "https://identityapi.pelicanidentity.com";
|
|
245
245
|
|
|
246
246
|
// src/engine/engine.ts
|
|
247
247
|
var PelicanAuthentication = class {
|
|
@@ -280,6 +280,7 @@ var PelicanAuthentication = class {
|
|
|
280
280
|
async start() {
|
|
281
281
|
if (this.stateMachine.current !== "idle") return;
|
|
282
282
|
this.resetSession();
|
|
283
|
+
clearAuthSession();
|
|
283
284
|
this.stateMachine.transition("initializing");
|
|
284
285
|
try {
|
|
285
286
|
const relay = await this.fetchRelayUrl();
|
|
@@ -317,7 +318,8 @@ var PelicanAuthentication = class {
|
|
|
317
318
|
`${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`
|
|
318
319
|
);
|
|
319
320
|
if (!res.ok) {
|
|
320
|
-
|
|
321
|
+
const error = await res.text();
|
|
322
|
+
throw new Error(error);
|
|
321
323
|
}
|
|
322
324
|
const json = await res.json();
|
|
323
325
|
return json.relay_url;
|
|
@@ -351,7 +353,7 @@ var PelicanAuthentication = class {
|
|
|
351
353
|
const qr = await QRCode__default.default.toDataURL(JSON.stringify(payload), {
|
|
352
354
|
type: "image/png",
|
|
353
355
|
scale: 3,
|
|
354
|
-
color: { light: "#
|
|
356
|
+
color: { light: "#ffffff", dark: "#424242ff" }
|
|
355
357
|
});
|
|
356
358
|
this.emit("qr", qr);
|
|
357
359
|
}
|
|
@@ -457,7 +459,6 @@ var PelicanAuthentication = class {
|
|
|
457
459
|
}
|
|
458
460
|
/** Cleans up the current session state */
|
|
459
461
|
terminate(success) {
|
|
460
|
-
if (!this.transport) return;
|
|
461
462
|
if (!success) {
|
|
462
463
|
this.transport?.send({
|
|
463
464
|
type: "client-terminated",
|
|
@@ -477,6 +478,7 @@ var PelicanAuthentication = class {
|
|
|
477
478
|
}
|
|
478
479
|
restartIfContinuous() {
|
|
479
480
|
this.stateMachine.transition("idle");
|
|
481
|
+
this.transport?.close();
|
|
480
482
|
if (!this.config.continuousMode) return;
|
|
481
483
|
setTimeout(() => {
|
|
482
484
|
this.start();
|
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\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private isExplicitlyClosed = false;\n private url?: string;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.url = url;\n this.isExplicitlyClosed = false;\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => {\n this.reconnectAttempts = 0;\n this.handlers.onOpen?.();\n };\n this.socket.onmessage = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data);\n this.handlers.onMessage?.(data);\n } catch (err) {\n console.error(\"Failed to parse message\", err);\n }\n };\n this.socket.onerror = (e: Event) => {\n this.handlers.onError?.(e);\n };\n this.socket.onclose = (e: CloseEvent) => {\n this.handlers.onClose?.(e);\n\n // Only reconnect if the user didn't call .close() manually\n if (!this.isExplicitlyClosed) {\n this.attemptReconnect();\n }\n };\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"❌ Max reconnect attempts reached\");\n return;\n }\n\n this.reconnectAttempts++;\n\n const delay = Math.pow(2, this.reconnectAttempts) * 500;\n setTimeout(() => {\n if (this.url) this.connect(this.url);\n }, delay);\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.isExplicitlyClosed = true;\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://192.168.1.2: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?sessionID=${encodeURIComponent(\n this.sessionId\n )}&sessionKey=${encodeURIComponent(\n this.sessionKey\n )}&publicKey=${encodeURIComponent(this.config.publicKey)}&authType=${\n this.config.authType\n }&projectId=${encodeURIComponent(\n this.config.projectId\n )}&url=${encodeURIComponent(window.location.href)}`\n );\n } else {\n const qr = await QRCode.toDataURL(JSON.stringify(payload), {\n type: \"image/png\",\n scale: 3,\n color: { light: \"#FFFFF5\", dark: \"#121212\" },\n });\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 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 this.transport?.close();\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"idle\");\n }\n\n private restartIfContinuous() {\n this.stateMachine.transition(\"idle\");\n if (!this.config.continuousMode) return;\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\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private isExplicitlyClosed = false;\n private url?: string;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.url = url;\n this.isExplicitlyClosed = false;\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => {\n this.reconnectAttempts = 0;\n this.handlers.onOpen?.();\n };\n this.socket.onmessage = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data);\n this.handlers.onMessage?.(data);\n } catch (err) {\n console.error(\"Failed to parse message\", err);\n }\n };\n this.socket.onerror = (e: Event) => {\n this.handlers.onError?.(e);\n };\n this.socket.onclose = (e: CloseEvent) => {\n this.handlers.onClose?.(e);\n\n // Only reconnect if the user didn't call .close() manually\n if (!this.isExplicitlyClosed) {\n this.attemptReconnect();\n }\n };\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"❌ Max reconnect attempts reached\");\n return;\n }\n\n this.reconnectAttempts++;\n\n const delay = Math.pow(2, this.reconnectAttempts) * 500;\n setTimeout(() => {\n if (this.url) this.connect(this.url);\n }, delay);\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.isExplicitlyClosed = true;\n this.socket?.close();\n }\n}\n","export const BASEURL = \"https://identityapi.pelicanidentity.com\";\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 this.resetSession();\n clearAuthSession();\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 const error = await res.text();\n throw new Error(error);\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?sessionID=${encodeURIComponent(\n this.sessionId\n )}&sessionKey=${encodeURIComponent(\n this.sessionKey\n )}&publicKey=${encodeURIComponent(this.config.publicKey)}&authType=${\n this.config.authType\n }&projectId=${encodeURIComponent(\n this.config.projectId\n )}&url=${encodeURIComponent(window.location.href)}`\n );\n } else {\n const qr = await QRCode.toDataURL(JSON.stringify(payload), {\n type: \"image/png\",\n scale: 3,\n color: { light: \"#ffffff\", dark: \"#424242ff\" },\n });\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 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 (!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 this.transport?.close();\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"idle\");\n }\n\n private restartIfContinuous() {\n this.stateMachine.transition(\"idle\");\n this.transport?.close();\n if (!this.config.continuousMode) return;\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
|
@@ -234,7 +234,7 @@ var Transport = class {
|
|
|
234
234
|
};
|
|
235
235
|
|
|
236
236
|
// src/constants.ts
|
|
237
|
-
var BASEURL = "
|
|
237
|
+
var BASEURL = "https://identityapi.pelicanidentity.com";
|
|
238
238
|
|
|
239
239
|
// src/engine/engine.ts
|
|
240
240
|
var PelicanAuthentication = class {
|
|
@@ -273,6 +273,7 @@ var PelicanAuthentication = class {
|
|
|
273
273
|
async start() {
|
|
274
274
|
if (this.stateMachine.current !== "idle") return;
|
|
275
275
|
this.resetSession();
|
|
276
|
+
clearAuthSession();
|
|
276
277
|
this.stateMachine.transition("initializing");
|
|
277
278
|
try {
|
|
278
279
|
const relay = await this.fetchRelayUrl();
|
|
@@ -310,7 +311,8 @@ var PelicanAuthentication = class {
|
|
|
310
311
|
`${BASEURL}/relay?public_key=${publicKey}&auth_type=${authType}&project_id=${projectId}`
|
|
311
312
|
);
|
|
312
313
|
if (!res.ok) {
|
|
313
|
-
|
|
314
|
+
const error = await res.text();
|
|
315
|
+
throw new Error(error);
|
|
314
316
|
}
|
|
315
317
|
const json = await res.json();
|
|
316
318
|
return json.relay_url;
|
|
@@ -344,7 +346,7 @@ var PelicanAuthentication = class {
|
|
|
344
346
|
const qr = await QRCode.toDataURL(JSON.stringify(payload), {
|
|
345
347
|
type: "image/png",
|
|
346
348
|
scale: 3,
|
|
347
|
-
color: { light: "#
|
|
349
|
+
color: { light: "#ffffff", dark: "#424242ff" }
|
|
348
350
|
});
|
|
349
351
|
this.emit("qr", qr);
|
|
350
352
|
}
|
|
@@ -450,7 +452,6 @@ var PelicanAuthentication = class {
|
|
|
450
452
|
}
|
|
451
453
|
/** Cleans up the current session state */
|
|
452
454
|
terminate(success) {
|
|
453
|
-
if (!this.transport) return;
|
|
454
455
|
if (!success) {
|
|
455
456
|
this.transport?.send({
|
|
456
457
|
type: "client-terminated",
|
|
@@ -470,6 +471,7 @@ var PelicanAuthentication = class {
|
|
|
470
471
|
}
|
|
471
472
|
restartIfContinuous() {
|
|
472
473
|
this.stateMachine.transition("idle");
|
|
474
|
+
this.transport?.close();
|
|
473
475
|
if (!this.config.continuousMode) return;
|
|
474
476
|
setTimeout(() => {
|
|
475
477
|
this.start();
|
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":";;;;;AAQO,IAAM,gBAAN,MAAoB;AAAA,EACzB,oBAAA,GAA+B;AAC7B,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,WAAA,CAAY,EAAE,CAAA;AAC/B,IAAA,OAAO,aAAa,GAAG,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,gBAAA,CAAiB;AAAA,IACf,SAAA;AAAA,IACA;AAAA,GACF,EAGqB;AACnB,IAAA,MAAM,GAAA,GAAM,aAAa,SAAS,CAAA;AAClC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,WAAA,CAAY,EAAE,CAAA;AAEjC,IAAA,MAAM,YAAA,GAAe,IAAI,WAAA,EAAY,CAAE,OAAO,SAAS,CAAA;AAEvD,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,SAAA,CAAU,YAAA,EAAc,OAAO,GAAG,CAAA;AAE1D,IAAA,OAAO;AAAA,MACL,MAAA,EAAQ,aAAa,UAAU,CAAA;AAAA,MAC/B,KAAA,EAAO,aAAa,KAAK;AAAA,KAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,gBAAA,CAAiB;AAAA,IACf,SAAA;AAAA,IACA;AAAA,GACF,EAGkB;AAChB,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,aAAa,SAAS,CAAA;AAClC,MAAA,MAAM,eAAA,GAAkB,YAAA,CAAa,SAAA,CAAU,MAAM,CAAA;AACrD,MAAA,MAAM,UAAA,GAAa,YAAA,CAAa,SAAA,CAAU,KAAK,CAAA;AAE/C,MAAA,MAAM,YAAY,IAAA,CAAK,SAAA,CAAU,IAAA,CAAK,eAAA,EAAiB,YAAY,GAAG,CAAA;AAEtE,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,MAAM,IAAI,MAAM,mDAAmD,CAAA;AAAA,MACrE;AAEA,MAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY,CAAE,OAAO,SAAS,CAAA;AAClD,MAAA,OAAO,OAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,qBAAqB,KAAK,CAAA;AACxC,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACF;AAEA,IAAO,cAAA,GAAQ,aAAA;;;AC/Df,IAAM,cAAN,MAAkB;AAAA,EAAlB,WAAA,GAAA;AACE,IAAA,IAAA,CAAiB,MAAA,GAAS,eAAA;AAC1B,IAAA,IAAA,CAAiB,UAAA,GAAa,IAAI,EAAA,GAAK,GAAA;AACvC;AAAA,IAAA,IAAA,CAAQ,WAAA,uBACF,GAAA,EAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAKV,GAAA,CAAI,GAAA,EAAa,KAAA,EAAY,OAAA,GAA0B,EAAC,EAAS;AAC/D,IAAA,MAAM,EAAE,KAAA,GAAQ,IAAA,CAAK,UAAA,EAAY,iBAAA,GAAoB,MAAK,GAAI,OAAA;AAE9D,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,EAAI,GAAI,KAAA;AAC/B,IAAA,MAAM,IAAA,GAAO,EAAE,KAAA,EAAO,SAAA,EAAU;AAGhC,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,IAAI;AACF,QAAA,cAAA,CAAe,OAAA,CAAQ,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA;AAAA,MACrE,SAAS,KAAA,EAAO;AACd,QAAA,OAAA,CAAQ,IAAA,CAAK,6CAA6C,KAAK,CAAA;AAC/D,QAAA,IAAA,CAAK,SAAA,CAAU,KAAK,IAAI,CAAA;AAAA,MAC1B;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,SAAA,CAAU,KAAK,IAAI,CAAA;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,GAAA,EAAyB;AAE3B,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,eAAe,OAAA,CAAQ,CAAA,EAAG,KAAK,MAAM,CAAA,EAAG,GAAG,CAAA,CAAE,CAAA;AAC5D,MAAA,IAAI,MAAA,EAAQ;AACV,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAG9B,QAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA,CAAK,SAAA,EAAW;AAC/B,UAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AACf,UAAA,OAAO,IAAA;AAAA,QACT;AAEA,QAAA,OAAO,IAAA,CAAK,KAAA;AAAA,MACd;AAAA,IACF,SAAS,KAAA,EAAO;AAAA,IAEhB;AAGA,IAAA,OAAO,IAAA,CAAK,UAAU,GAAG,CAAA;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,GAAA,EAAmB;AACxB,IAAA,IAAI;AACF,MAAA,cAAA,CAAe,WAAW,CAAA,EAAG,IAAA,CAAK,MAAM,CAAA,EAAG,GAAG,CAAA,CAAE,CAAA;AAAA,IAClD,SAAS,KAAA,EAAO;AAAA,IAEhB;AACA,IAAA,IAAA,CAAK,WAAA,CAAY,OAAO,GAAG,CAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAc;AAEZ,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,IAAA,CAAK,cAAc,CAAA,CAAE,OAAA,CAAQ,CAAC,GAAA,KAAQ;AAC3C,QAAA,IAAI,GAAA,CAAI,UAAA,CAAW,IAAA,CAAK,MAAM,CAAA,EAAG;AAC/B,UAAA,cAAA,CAAe,WAAW,GAAG,CAAA;AAAA,QAC/B;AAAA,MACF,CAAC,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AAAA,IAEhB;AAGA,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAMQ,SAAA,CACN,KACA,IAAA,EACM;AACN,IAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA;AAG9B,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,GAAA,EAAI;AACtC,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IAAA,CAAK,WAAA,CAAY,OAAO,GAAG,CAAA;AAAA,IAC7B,GAAG,GAAG,CAAA;AAAA,EACR;AAAA,EAEQ,UAAU,GAAA,EAAyB;AACzC,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA;AACrC,IAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,IAAA,CAAK,SAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,WAAA,CAAY,OAAO,GAAG,CAAA;AAC3B,MAAA,OAAO,IAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AACF,CAAA;AAGA,IAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAWzB,IAAM,gBAAA,GAAmB,CAC9B,SAAA,EACA,UAAA,EACA,KAAA,KACS;AACT,EAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,EAAE,SAAA,EAAW,YAAW,EAAG,EAAE,OAAO,CAAA;AAC7D;AAEO,IAAM,iBAAiB,MAA0B;AACtD,EAAA,OAAO,OAAA,CAAQ,IAAI,SAAS,CAAA;AAC9B;AAEO,IAAM,mBAAmB,MAAY;AAC1C,EAAA,OAAA,CAAQ,OAAO,SAAS,CAAA;AAC1B;AAEO,IAAM,mBAAmB,MAAY;AAC1C,EAAA,OAAA,CAAQ,KAAA,EAAM;AAChB;;;AC1JO,IAAM,eAAN,MAAmB;AAAA,EAAnB,WAAA,GAAA;AACL,IAAA,IAAA,CAAQ,KAAA,GAA0B,MAAA;AAClC,IAAA,IAAA,CAAQ,SAAA,uBAAgB,GAAA,EAAmC;AAAA,EAAA;AAAA,EAE3D,IAAI,OAAA,GAAU;AACZ,IAAA,OAAO,IAAA,CAAK,KAAA;AAAA,EACd;AAAA,EAEA,WAAW,IAAA,EAAwB;AACjC,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAA;AACb,IAAA,IAAA,CAAK,UAAU,OAAA,CAAQ,CAAC,CAAA,KAAM,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,EACvC;AAAA,EAEA,UAAU,EAAA,EAAmC;AAC3C,IAAA,IAAA,CAAK,SAAA,CAAU,IAAI,EAAE,CAAA;AACrB,IAAA,OAAO,MAAM,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,EAAE,CAAA;AAAA,EACvC;AACF;;;ACVO,IAAM,YAAN,MAAgB;AAAA,EAQrB,YAAY,QAAA,EAA6B;AALzC,IAAA,IAAA,CAAQ,iBAAA,GAAoB,CAAA;AAC5B,IAAA,IAAA,CAAQ,oBAAA,GAAuB,CAAA;AAC/B,IAAA,IAAA,CAAQ,kBAAA,GAAqB,KAAA;AAI3B,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAAA,EAClB;AAAA,EAEA,QAAQ,GAAA,EAAa;AACnB,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AACX,IAAA,IAAA,CAAK,kBAAA,GAAqB,KAAA;AAC1B,IAAA,IAAA,CAAK,MAAA,GAAS,IAAI,SAAA,CAAU,GAAG,CAAA;AAE/B,IAAA,IAAA,CAAK,MAAA,CAAO,SAAS,MAAM;AACzB,MAAA,IAAA,CAAK,iBAAA,GAAoB,CAAA;AACzB,MAAA,IAAA,CAAK,SAAS,MAAA,IAAS;AAAA,IACzB,CAAA;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,SAAA,GAAY,CAAC,CAAA,KAAoB;AAC3C,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,IAAI,CAAA;AAC9B,QAAA,IAAA,CAAK,QAAA,CAAS,YAAY,IAAI,CAAA;AAAA,MAChC,SAAS,GAAA,EAAK;AACZ,QAAA,OAAA,CAAQ,KAAA,CAAM,2BAA2B,GAAG,CAAA;AAAA,MAC9C;AAAA,IACF,CAAA;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,OAAA,GAAU,CAAC,CAAA,KAAa;AAClC,MAAA,IAAA,CAAK,QAAA,CAAS,UAAU,CAAC,CAAA;AAAA,IAC3B,CAAA;AACA,IAAA,IAAA,CAAK,MAAA,CAAO,OAAA,GAAU,CAAC,CAAA,KAAkB;AACvC,MAAA,IAAA,CAAK,QAAA,CAAS,UAAU,CAAC,CAAA;AAGzB,MAAA,IAAI,CAAC,KAAK,kBAAA,EAAoB;AAC5B,QAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,MACxB;AAAA,IACF,CAAA;AAAA,EACF;AAAA,EAEQ,gBAAA,GAAmB;AACzB,IAAA,IAAI,IAAA,CAAK,iBAAA,IAAqB,IAAA,CAAK,oBAAA,EAAsB;AACvD,MAAA,OAAA,CAAQ,MAAM,uCAAkC,CAAA;AAChD,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,iBAAA,EAAA;AAEL,IAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,iBAAiB,CAAA,GAAI,GAAA;AACpD,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IAAI,IAAA,CAAK,GAAA,EAAK,IAAA,CAAK,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA,IACrC,GAAG,KAAK,CAAA;AAAA,EACV;AAAA,EACA,KAAK,OAAA,EAAyB;AAC5B,IAAA,IAAI,IAAA,CAAK,MAAA,EAAQ,UAAA,KAAe,SAAA,CAAU,IAAA,EAAM;AAC9C,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,SAAA,CAAU,OAAO,CAAC,CAAA;AAAA,IAC1C;AAAA,EACF;AAAA,EAEA,KAAA,GAAQ;AACN,IAAA,IAAA,CAAK,kBAAA,GAAqB,IAAA;AAC1B,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAA,EACrB;AACF;;;AC1EO,IAAM,OAAA,GAAU;;;ACyBhB,IAAM,wBAAN,MAA4B;AAAA,EAgBjC,YAAY,MAAA,EAA2B;AAfvC,IAAA,IAAA,CAAiB,MAAA,GAAS,IAAI,cAAA,EAAc;AAC5C,IAAA,IAAA,CAAiB,YAAA,GAAe,IAAI,YAAA,EAAa;AAGjD,IAAA,IAAA,CAAQ,SAAA,GAAY,EAAA;AACpB,IAAA,IAAA,CAAQ,UAAA,GAA4B,IAAA;AAIpC,IAAA,IAAA,CAAQ,YAEJ,EAAC;AAKH,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,EAAW,MAAM,IAAI,MAAM,mBAAmB,CAAA;AAC1D,IAAA,IAAI,CAAC,MAAA,CAAO,SAAA,EAAW,MAAM,IAAI,MAAM,mBAAmB,CAAA;AAC1D,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,EAAU,MAAM,IAAI,MAAM,kBAAkB,CAAA;AAExD,IAAA,IAAA,CAAK,MAAA,GAAS;AAAA,MACZ,cAAA,EAAgB,KAAA;AAAA,MAChB,WAAA,EAAa,KAAA;AAAA,MACb,GAAG;AAAA,KACL;AAGA,IAAA,IAAA,CAAK,YAAA,CAAa,UAAU,CAAC,CAAA,KAAM,KAAK,IAAA,CAAK,OAAA,EAAS,CAAC,CAAC,CAAA;AACxD,IAAA,IAAA,CAAK,wBAAA,EAAyB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,EAAA,CACE,OACA,EAAA,EACA;AAlEJ,IAAA,IAAA,EAAA;AAmEI,IAAA,CAAA,EAAA,GAAA,IAAA,CAAK,SAAA,EAAL,KAAA,CAAA,KAAA,EAAA,CAAA,KAAA,CAAA,mBAA0B,IAAI,GAAA,EAAI,CAAA;AAClC,IAAA,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,CAAG,GAAA,CAAI,EAAE,CAAA;AAC7B,IAAA,OAAO,MAAM,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,CAAG,OAAO,EAAE,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAA,GAAQ;AACZ,IAAA,IAAI,IAAA,CAAK,YAAA,CAAa,OAAA,KAAY,MAAA,EAAQ;AAE1C,IAAA,IAAA,CAAK,YAAA,EAAa;AAClB,IAAA,IAAA,CAAK,YAAA,CAAa,WAAW,cAAc,CAAA;AAE3C,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,aAAA,EAAc;AAGvC,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA,CAAK,MAAA,CAAO,oBAAA,EAAqB;AACnD,MAAA,IAAA,CAAK,SAAA,GAAY,MAAA,CAAO,UAAA,EAAW,GAAI,OAAO,UAAA,EAAW;AAEzD,MAAA,IAAA,CAAK,SAAA,GAAY,IAAI,SAAA,CAAU;AAAA,QAC7B,QAAQ,MAAM;AAEZ,UAAA,IAAA,CAAK,UAAW,IAAA,CAAK;AAAA,YACnB,IAAA,EAAM,UAAA;AAAA,YACN,WAAW,IAAA,CAAK,SAAA;AAAA,YAChB,GAAG,IAAA,CAAK;AAAA,WACT,CAAA;AACD,UAAA,IAAA,CAAK,YAAA,CAAa,WAAW,eAAe,CAAA;AAAA,QAC9C,CAAA;AAAA,QACA,SAAA,EAAW,CAAC,GAAA,KAAwB,IAAA,CAAK,cAAc,GAAG,CAAA;AAAA,QAC1D,SAAS,MAAM,IAAA,CAAK,KAAK,IAAI,KAAA,CAAM,6BAA6B,CAAC;AAAA,OAClE,CAAA;AAED,MAAA,IAAA,CAAK,SAAA,CAAU,QAAQ,KAAK,CAAA;AAC5B,MAAA,MAAM,KAAK,cAAA,EAAe;AAAA,IAC5B,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,KAAK,GAAA,YAAe,KAAA,GAAQ,MAAM,IAAI,KAAA,CAAM,cAAc,CAAC,CAAA;AAAA,IAClE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,GAAO;AACL,IAAA,IAAA,CAAK,UAAU,KAAK,CAAA;AAAA,EACtB;AAAA;AAAA;AAAA,EAKA,MAAc,aAAA,GAAiC;AAC7C,IAAA,MAAM,EAAE,SAAA,EAAW,SAAA,EAAW,QAAA,KAAa,IAAA,CAAK,MAAA;AAChD,IAAA,MAAM,MAAM,MAAM,KAAA;AAAA,MAChB,GAAG,OAAO,CAAA,kBAAA,EAAqB,SAAS,CAAA,WAAA,EAAc,QAAQ,eAAe,SAAS,CAAA;AAAA,KACxF;AAEA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAAA,IAC7C;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAAA,GAAiB;AAC7B,IAAA,MAAM,OAAA,GAAU;AAAA,MACd,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,YAAY,IAAA,CAAK,UAAA;AAAA,MACjB,SAAA,EAAW,KAAK,MAAA,CAAO,SAAA;AAAA,MACvB,QAAA,EAAU,KAAK,MAAA,CAAO,QAAA;AAAA,MACtB,SAAA,EAAW,KAAK,MAAA,CAAO,SAAA;AAAA,MACvB,GAAA,EAAK,OAAO,QAAA,CAAS;AAAA,KACvB;AAEA,IAAA,MAAM,WAAA,GACJ,KAAK,MAAA,CAAO,WAAA,IACZ,CAAC,sBAAA,CAAuB,IAAA,CAAK,UAAU,SAAS,CAAA;AAElD,IAAA,IAAI,CAAC,WAAA,IAAe,IAAA,CAAK,UAAA,EAAY;AAEnC,MAAA,gBAAA,CAAiB,IAAA,CAAK,SAAA,EAAW,IAAA,CAAK,UAAA,EAAY,IAAI,GAAM,CAAA;AAC5D,MAAA,IAAA,CAAK,IAAA;AAAA,QACH,UAAA;AAAA,QACA,CAAA,wCAAA,EAA2C,kBAAA;AAAA,UACzC,IAAA,CAAK;AAAA,SACN,CAAA,YAAA,EAAe,kBAAA;AAAA,UACd,IAAA,CAAK;AAAA,SACN,CAAA,WAAA,EAAc,kBAAA,CAAmB,IAAA,CAAK,MAAA,CAAO,SAAS,CAAC,CAAA,UAAA,EACtD,IAAA,CAAK,MAAA,CAAO,QACd,CAAA,WAAA,EAAc,kBAAA;AAAA,UACZ,KAAK,MAAA,CAAO;AAAA,SACb,CAAA,KAAA,EAAQ,kBAAA,CAAmB,MAAA,CAAO,QAAA,CAAS,IAAI,CAAC,CAAA;AAAA,OACnD;AAAA,IACF,CAAA,MAAO;AACL,MAAA,MAAM,KAAK,MAAM,MAAA,CAAO,UAAU,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,EAAG;AAAA,QACzD,IAAA,EAAM,WAAA;AAAA,QACN,KAAA,EAAO,CAAA;AAAA,QACP,KAAA,EAAO,EAAE,KAAA,EAAO,SAAA,EAAW,MAAM,SAAA;AAAU,OAC5C,CAAA;AACD,MAAA,IAAA,CAAK,IAAA,CAAK,MAAM,EAAE,CAAA;AAAA,IACpB;AAAA,EACF;AAAA;AAAA,EAGQ,cAAc,GAAA,EAAqB;AACzC,IAAA,QAAQ,IAAI,IAAA;AAAM,MAChB,KAAK,QAAA;AACH,QAAA,IAAA,CAAK,YAAA,CAAa,WAAW,QAAQ,CAAA;AACrC,QAAA,IAAA,CAAK,UAAW,IAAA,CAAK;AAAA,UACnB,IAAA,EAAM,cAAA;AAAA,UACN,WAAW,IAAA,CAAK,SAAA;AAAA,UAChB,GAAG,IAAA,CAAK,MAAA;AAAA,UACR,GAAA,EAAK,OAAO,QAAA,CAAS;AAAA,SACtB,CAAA;AACD,QAAA;AAAA,MAEF,KAAK,oBAAA;AAEH,QAAA,IAAA,CAAK,kBAAkB,GAAG,CAAA;AAC1B,QAAA;AAAA,MAEF,KAAK,kBAAA;AACH,QAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,iDAAiD,CAAC,CAAA;AACtE,QAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,QAAA;AAAA,MAEF,KAAK,WAAA;AAEH,QAAA,IAAA,CAAK,UAAU,IAAI,CAAA;AACnB,QAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,QAAA;AAAA;AACJ,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,GAAA,EAAqB;AAC7C,IAAA,IAAI,CAAC,KAAK,UAAA,IAAc,CAAC,IAAI,MAAA,IAAU,CAAC,IAAI,KAAA,EAAO;AACjD,MAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,gCAAgC,CAAC,CAAA;AACrD,MAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,gBAAA,CAAiB;AAAA,QAC7C,WAAW,EAAE,MAAA,EAAQ,IAAI,MAAA,EAAQ,KAAA,EAAO,IAAI,KAAA,EAAM;AAAA,QAClD,WAAW,IAAA,CAAK;AAAA,OACjB,CAAA;AAED,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,6BAA6B,CAAC,CAAA;AAClD,QAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,QAAA;AAAA,MACF;AAEA,MAAA,MAAM,MAAA,GAAyB,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA;AAEnD,MAAA,IAAA,CAAK,IAAA,CAAK,WAAW,MAAM,CAAA;AAC3B,MAAA,IAAA,CAAK,YAAA,CAAa,WAAW,eAAe,CAAA;AAG5C,MAAA,IAAA,CAAK,WAAW,IAAA,CAAK;AAAA,QACnB,IAAA,EAAM,SAAA;AAAA,QACN,WAAW,IAAA,CAAK;AAAA,OACjB,CAAA;AAAA,IACH,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,uCAAuC,CAAC,CAAA;AAC5D,MAAA,IAAA,CAAK,mBAAA,EAAoB;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,eAAe,MAAA,EAAqB;AAChD,IAAA,IAAI;AACF,MAAA,MAAM,MAAM,MAAM,KAAA;AAAA,QAChB,CAAA,EAAG,OAAO,CAAA,oBAAA,EAAuB,MAAA,CAAO,SAAS,CAAA;AAAA,OACnD;AAEA,MAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,IAAI,MAAM,iBAAiB,CAAA;AAE9C,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,MAAM,SAAA,GAAY,IAAA,CAAK,MAAA,CAAO,gBAAA,CAAiB;AAAA,QAC7C,WAAW,EAAE,MAAA,EAAQ,KAAK,MAAA,EAAQ,KAAA,EAAO,KAAK,KAAA,EAAM;AAAA,QACpD,WAAW,MAAA,CAAO;AAAA,OACnB,CAAA;AAED,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,sBAAsB,CAAC,CAAA;AAC3C,QAAA,IAAA,CAAK,mBAAA,EAAoB;AACzB,QAAA;AAAA,MACF;AACA,MAAA,MAAM,MAAA,GAAyB,IAAA,CAAK,KAAA,CAAM,SAAS,CAAA;AACnD,MAAA,IAAA,CAAK,IAAA,CAAK,WAAW,MAAM,CAAA;AAC3B,MAAA,gBAAA,EAAiB;AAAA,IACnB,CAAA,CAAA,MAAQ;AACN,MAAA,IAAA,CAAK,IAAA,CAAK,IAAI,KAAA,CAAM,yBAAyB,CAAC,CAAA;AAC9C,MAAA,IAAA,CAAK,mBAAA,EAAoB;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,wBAAA,GAA2B;AACjC,IAAA,IAAA,CAAK,oBAAoB,YAAY;AACnC,MAAA,IAAI,QAAA,CAAS,oBAAoB,SAAA,EAAW;AAE5C,MAAA,MAAM,SAAS,cAAA,EAAe;AAC9B,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,MAAM,IAAA,CAAK,eAAe,MAAM,CAAA;AAAA,IAClC,CAAA;AAEA,IAAA,QAAA,CAAS,gBAAA,CAAiB,kBAAA,EAAoB,IAAA,CAAK,iBAAiB,CAAA;AAAA,EACtE;AAAA,EAEQ,wBAAA,GAA2B;AACjC,IAAA,IAAI,KAAK,iBAAA,EAAmB;AAC1B,MAAA,QAAA,CAAS,mBAAA,CAAoB,kBAAA,EAAoB,IAAA,CAAK,iBAAiB,CAAA;AACvE,MAAA,IAAA,CAAK,iBAAA,GAAoB,MAAA;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA,EAGQ,UAAU,OAAA,EAAkB;AAClC,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACrB,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,IAAA,CAAK,WAAW,IAAA,CAAK;AAAA,QACnB,IAAA,EAAM,mBAAA;AAAA,QACN,WAAW,IAAA,CAAK,SAAA;AAAA,QAChB,SAAA,EAAW,KAAK,MAAA,CAAO,SAAA;AAAA,QACvB,SAAA,EAAW,KAAK,MAAA,CAAO,SAAA;AAAA,QACvB,QAAA,EAAU,KAAK,MAAA,CAAO;AAAA,OACvB,CAAA;AAAA,IACH;AAEA,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AACtB,IAAA,gBAAA,EAAiB;AACjB,IAAA,IAAA,CAAK,YAAA,EAAa;AAElB,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,cAAA,EAAgB;AAC/B,MAAA,IAAA,CAAK,wBAAA,EAAyB;AAAA,IAChC;AACA,IAAA,IAAA,CAAK,YAAA,CAAa,UAAA,CAAW,OAAA,GAAU,WAAA,GAAc,MAAM,CAAA;AAAA,EAC7D;AAAA,EAEQ,mBAAA,GAAsB;AAC5B,IAAA,IAAA,CAAK,YAAA,CAAa,WAAW,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,cAAA,EAAgB;AACjC,IAAA,UAAA,CAAW,MAAM;AACf,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IACb,GAAG,GAAG,CAAA;AAAA,EACR;AAAA,EAEQ,YAAA,GAAe;AACrB,IAAA,IAAA,CAAK,SAAA,GAAY,EAAA;AACjB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,EACpB;AAAA;AAAA,EAGA,OAAA,GAAU;AACR,IAAA,IAAA,CAAK,wBAAA,EAAyB;AAC9B,IAAA,IAAA,CAAK,WAAW,KAAA,EAAM;AACtB,IAAA,IAAA,CAAK,YAAY,EAAC;AAAA,EACpB;AAAA,EAEQ,IAAA,CACN,OACA,OAAA,EACA;AACA,IAAA,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,EAAG,OAAA,CAAQ,CAAC,EAAA,KAAO,EAAA,CAAG,OAAO,CAAC,CAAA;AAAA,EACpD;AAAA,EAEQ,KAAK,GAAA,EAAY;AACvB,IAAA,IAAA,CAAK,IAAA,CAAK,SAAS,GAAG,CAAA;AACtB,IAAA,IAAA,CAAK,YAAA,CAAa,WAAW,OAAO,CAAA;AAAA,EACtC;AACF","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\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private isExplicitlyClosed = false;\n private url?: string;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.url = url;\n this.isExplicitlyClosed = false;\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => {\n this.reconnectAttempts = 0;\n this.handlers.onOpen?.();\n };\n this.socket.onmessage = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data);\n this.handlers.onMessage?.(data);\n } catch (err) {\n console.error(\"Failed to parse message\", err);\n }\n };\n this.socket.onerror = (e: Event) => {\n this.handlers.onError?.(e);\n };\n this.socket.onclose = (e: CloseEvent) => {\n this.handlers.onClose?.(e);\n\n // Only reconnect if the user didn't call .close() manually\n if (!this.isExplicitlyClosed) {\n this.attemptReconnect();\n }\n };\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"❌ Max reconnect attempts reached\");\n return;\n }\n\n this.reconnectAttempts++;\n\n const delay = Math.pow(2, this.reconnectAttempts) * 500;\n setTimeout(() => {\n if (this.url) this.connect(this.url);\n }, delay);\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.isExplicitlyClosed = true;\n this.socket?.close();\n }\n}\n","export const BASEURL = \"http://192.168.1.2: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?sessionID=${encodeURIComponent(\n this.sessionId\n )}&sessionKey=${encodeURIComponent(\n this.sessionKey\n )}&publicKey=${encodeURIComponent(this.config.publicKey)}&authType=${\n this.config.authType\n }&projectId=${encodeURIComponent(\n this.config.projectId\n )}&url=${encodeURIComponent(window.location.href)}`\n );\n } else {\n const qr = await QRCode.toDataURL(JSON.stringify(payload), {\n type: \"image/png\",\n scale: 3,\n color: { light: \"#FFFFF5\", dark: \"#121212\" },\n });\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 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 this.transport?.close();\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"idle\");\n }\n\n private restartIfContinuous() {\n this.stateMachine.transition(\"idle\");\n if (!this.config.continuousMode) return;\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\nexport class Transport {\n private socket?: WebSocket;\n private handlers: TransportHandlers;\n private reconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private isExplicitlyClosed = false;\n private url?: string;\n\n constructor(handlers: TransportHandlers) {\n this.handlers = handlers;\n }\n\n connect(url: string) {\n this.url = url;\n this.isExplicitlyClosed = false;\n this.socket = new WebSocket(url);\n\n this.socket.onopen = () => {\n this.reconnectAttempts = 0;\n this.handlers.onOpen?.();\n };\n this.socket.onmessage = (e: MessageEvent) => {\n try {\n const data = JSON.parse(e.data);\n this.handlers.onMessage?.(data);\n } catch (err) {\n console.error(\"Failed to parse message\", err);\n }\n };\n this.socket.onerror = (e: Event) => {\n this.handlers.onError?.(e);\n };\n this.socket.onclose = (e: CloseEvent) => {\n this.handlers.onClose?.(e);\n\n // Only reconnect if the user didn't call .close() manually\n if (!this.isExplicitlyClosed) {\n this.attemptReconnect();\n }\n };\n }\n\n private attemptReconnect() {\n if (this.reconnectAttempts >= this.maxReconnectAttempts) {\n console.error(\"❌ Max reconnect attempts reached\");\n return;\n }\n\n this.reconnectAttempts++;\n\n const delay = Math.pow(2, this.reconnectAttempts) * 500;\n setTimeout(() => {\n if (this.url) this.connect(this.url);\n }, delay);\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.isExplicitlyClosed = true;\n this.socket?.close();\n }\n}\n","export const BASEURL = \"https://identityapi.pelicanidentity.com\";\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 this.resetSession();\n clearAuthSession();\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 const error = await res.text();\n throw new Error(error);\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?sessionID=${encodeURIComponent(\n this.sessionId\n )}&sessionKey=${encodeURIComponent(\n this.sessionKey\n )}&publicKey=${encodeURIComponent(this.config.publicKey)}&authType=${\n this.config.authType\n }&projectId=${encodeURIComponent(\n this.config.projectId\n )}&url=${encodeURIComponent(window.location.href)}`\n );\n } else {\n const qr = await QRCode.toDataURL(JSON.stringify(payload), {\n type: \"image/png\",\n scale: 3,\n color: { light: \"#ffffff\", dark: \"#424242ff\" },\n });\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 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 (!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 this.transport?.close();\n clearAuthSession();\n this.resetSession();\n\n if (!this.config.continuousMode) {\n this.detachVisibilityRecovery();\n }\n this.stateMachine.transition(success ? \"confirmed\" : \"idle\");\n }\n\n private restartIfContinuous() {\n this.stateMachine.transition(\"idle\");\n this.transport?.close();\n if (!this.config.continuousMode) return;\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"]}
|