@pelican-identity/auth-core 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +570 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,570 @@
1
+ # Pelican Auth SDK
2
+
3
+ A modern, event-driven JavaScript/TypeScript SDK for integrating Pelican Vault authentication into your web applications.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Event-Driven Architecture** - Subscribe to authentication lifecycle events
8
+ - 🔄 **Continuous Mode** - Automatic session restart for repeated authentication
9
+ - 📱 **Multi-Platform** - Supports both QR code (desktop) and deep linking (mobile)
10
+ - 🔐 **Secure** - End-to-end encrypted authentication using TweetNaCl
11
+ - 🔌 **WebSocket Transport** - Real-time communication with Pelican Vault
12
+ - 🎨 **Framework Agnostic** - Works with React, Vue, Svelte, or vanilla JS
13
+ - 📦 **Type-Safe** - Full TypeScript support with comprehensive type definitions
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm @pelican-identity/sdk
19
+ # or
20
+ yarn add @pelican-identity/sdk
21
+ # or
22
+ pnpm add @pelican-identity/sdk
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ### Basic Usage
28
+
29
+ ```typescript
30
+ import { PelicanAuth } from "@pelican-identity/sdk";
31
+
32
+ // Initialize the SDK
33
+ const auth = new PelicanAuth({
34
+ publicKey: "your-public-key",
35
+ projectId: "your-project-id",
36
+ authType: "login", // 'login' | 'signup' | 'id-verification'
37
+ });
38
+
39
+ // Listen for authentication success
40
+ auth.on("success", (identity) => {
41
+ console.log("User authenticated:", identity);
42
+ // Handle successful authentication
43
+ });
44
+
45
+ // Listen for errors
46
+ auth.on("error", (error) => {
47
+ console.error("Authentication failed:", error);
48
+ });
49
+
50
+ // Listen for QR code (desktop) or deep link (mobile)
51
+ auth.on("qr", (qrDataUrl) => {
52
+ // Display QR code image
53
+ document.getElementById("qr").src = qrDataUrl;
54
+ });
55
+
56
+ auth.on("deeplink", (url) => {
57
+ // Redirect to Pelican Vault app
58
+ window.location.href = url;
59
+ });
60
+
61
+ // Start authentication
62
+ auth.start();
63
+ ```
64
+
65
+ ## API Reference
66
+
67
+ ### Constructor
68
+
69
+ ```typescript
70
+ new PelicanAuth(config: PelicanAuthConfig)
71
+ ```
72
+
73
+ #### Configuration Options
74
+
75
+ | Option | Type | Required | Default | Description |
76
+ | ---------------- | ------------------------------------------ | -------- | ------- | ------------------------------------ |
77
+ | `publicKey` | `string` | ✅ Yes | - | Your Pelican Vault public key |
78
+ | `projectId` | `string` | ✅ Yes | - | Your project identifier |
79
+ | `authType` | `'login' \| 'signup' \| 'id-verification'` | ✅ Yes | - | Type of authentication flow |
80
+ | `continuousMode` | `boolean` | ❌ No | `false` | Auto-restart after successful auth |
81
+ | `forceQRCode` | `boolean` | ❌ No | `false` | Force QR code even on mobile devices |
82
+
83
+ ### Methods
84
+
85
+ #### `start(): Promise<void>`
86
+
87
+ Initiates the authentication flow.
88
+
89
+ ```typescript
90
+ await auth.start();
91
+ ```
92
+
93
+ #### `stop(): void`
94
+
95
+ Stops the authentication flow and cleans up resources.
96
+
97
+ ```typescript
98
+ auth.stop();
99
+ ```
100
+
101
+ #### `on<K>(event: K, callback: Listener<K>): () => void`
102
+
103
+ Subscribe to authentication events. Returns an unsubscribe function.
104
+
105
+ ```typescript
106
+ const unsubscribe = auth.on("success", (identity) => {
107
+ console.log(identity);
108
+ });
109
+
110
+ // Later: unsubscribe
111
+ unsubscribe();
112
+ ```
113
+
114
+ #### `destroy(): void`
115
+
116
+ Completely destroys the SDK instance, removing all listeners and cleanup.
117
+
118
+ ```typescript
119
+ auth.destroy();
120
+ ```
121
+
122
+ ### Events
123
+
124
+ The SDK emits the following events:
125
+
126
+ #### `state`
127
+
128
+ Emitted when the authentication state changes.
129
+
130
+ ```typescript
131
+ auth.on("state", (state) => {
132
+ console.log("Current state:", state);
133
+ });
134
+ ```
135
+
136
+ **States:**
137
+
138
+ - `idle` - Initial state
139
+ - `initializing` - Fetching relay server
140
+ - `awaiting-pair` - Waiting for mobile device to scan
141
+ - `paired` - Device connected
142
+ - `authenticated` - User authenticated
143
+ - `confirmed` - Authentication confirmed
144
+ - `terminated` - Session ended
145
+ - `error` - Error occurred
146
+
147
+ #### `success`
148
+
149
+ Emitted when authentication succeeds.
150
+
151
+ ```typescript
152
+ auth.on("success", (identity: IdentityResult) => {
153
+ console.log("User ID:", identity.user_id);
154
+ console.log("Email:", identity.user_data.email.value);
155
+ console.log("Phone:", identity.user_data.phone.number);
156
+ console.log("First Name:", identity.user_data.first_name);
157
+
158
+ console.log("ID Verification:", identity.id_verification);
159
+ });
160
+ ```
161
+
162
+ **IdentityResult:**
163
+
164
+ ```typescript
165
+ interface IdentityResult {
166
+ user_id: string;
167
+ user_data: IUserData;
168
+ id_verification: IKycData;
169
+ id_downloadurls?: { front_of_card?: string; back_of_card?: string };
170
+ }
171
+ ```
172
+
173
+ #### `error`
174
+
175
+ Emitted when an error occurs.
176
+
177
+ ```typescript
178
+ auth.on("error", (error: Error) => {
179
+ console.error("Error:", error.message);
180
+ });
181
+ ```
182
+
183
+ #### `qr`
184
+
185
+ Emitted on desktop with QR code data URL.
186
+
187
+ ```typescript
188
+ auth.on("qr", (dataUrl: string) => {
189
+ // Display in an <img> tag
190
+ document.getElementById("qr").src = dataUrl;
191
+ });
192
+ ```
193
+
194
+ #### `deeplink`
195
+
196
+ Emitted on mobile with deep link URL.
197
+
198
+ ```typescript
199
+ auth.on("deeplink", (url: string) => {
200
+ // Open Pelican Vault app
201
+ window.location.href = url;
202
+ // Or use as href in a button
203
+ document.getElementById("login-btn").href = url;
204
+ });
205
+ ```
206
+
207
+ ## Usage Examples
208
+
209
+ ### React Hook
210
+
211
+ ```typescript
212
+ import { useEffect, useState } from "react";
213
+ import { PelicanAuth } from "@pelican-identity/sdk";
214
+
215
+ function usePelicanAuth(config) {
216
+ const [state, setState] = useState("idle");
217
+ const [qrCode, setQrCode] = useState(null);
218
+ const [deepLink, setDeepLink] = useState(null);
219
+ const [error, setError] = useState(null);
220
+
221
+ useEffect(() => {
222
+ const auth = new PelicanAuth(config);
223
+
224
+ auth.on("state", setState);
225
+ auth.on("qr", setQrCode);
226
+ auth.on("deeplink", setDeepLink);
227
+ auth.on("error", setError);
228
+
229
+ auth.on("success", (identity) => {
230
+ console.log("Authenticated:", identity);
231
+ config.onSuccess?.(identity);
232
+ });
233
+
234
+ return () => auth.destroy();
235
+ }, []);
236
+
237
+ return { state, qrCode, deepLink, error };
238
+ }
239
+
240
+ // Usage
241
+ function LoginButton() {
242
+ const { state, qrCode, deepLink } = usePelicanAuth({
243
+ publicKey: "your-key",
244
+ projectId: "your-project",
245
+ authType: "login",
246
+ onSuccess: (identity) => {
247
+ // Handle login
248
+ },
249
+ });
250
+
251
+ if (qrCode) {
252
+ return <img src={qrCode} alt="Scan to login" />;
253
+ }
254
+
255
+ if (deepLink) {
256
+ return <a href={deepLink}>Open Pelican App</a>;
257
+ }
258
+
259
+ return <button onClick={() => auth.start()}>Login</button>;
260
+ }
261
+ ```
262
+
263
+ ### Vue Composition API
264
+
265
+ ```vue
266
+ <script setup>
267
+ import { ref, onMounted, onUnmounted } from "vue";
268
+ import { PelicanAuth } from "@pelican-identity/pelican-sdk";
269
+
270
+ const props = defineProps(["publicKey", "projectId", "authType"]);
271
+ const emit = defineEmits(["success", "error"]);
272
+
273
+ const state = ref("idle");
274
+ const qrCode = ref(null);
275
+ const deepLink = ref(null);
276
+
277
+ let auth;
278
+
279
+ onMounted(() => {
280
+ auth = new PelicanAuth({
281
+ publicKey: props.publicKey,
282
+ projectId: props.projectId,
283
+ authType: props.authType,
284
+ });
285
+
286
+ auth.on("state", (s) => (state.value = s));
287
+ auth.on("qr", (qr) => (qrCode.value = qr));
288
+ auth.on("deeplink", (link) => (deepLink.value = link));
289
+ auth.on("success", (identity) => emit("success", identity));
290
+ auth.on("error", (err) => emit("error", err));
291
+
292
+ auth.start();
293
+ });
294
+
295
+ onUnmounted(() => {
296
+ auth?.destroy();
297
+ });
298
+ </script>
299
+
300
+ <template>
301
+ <div>
302
+ <img v-if="qrCode" :src="qrCode" alt="QR Code" />
303
+ <a v-else-if="deepLink" :href="deepLink">Open App</a>
304
+ <p v-else>{{ state }}</p>
305
+ </div>
306
+ </template>
307
+ ```
308
+
309
+ ### Vanilla JavaScript
310
+
311
+ ```javascript
312
+ import { PelicanAuth } from "@pelican-identity/sdk";
313
+
314
+ // Create container
315
+ const container = document.getElementById("auth-container");
316
+
317
+ // Initialize
318
+ const auth = new PelicanAuth({
319
+ publicKey: "your-key",
320
+ projectId: "your-project",
321
+ authType: "login",
322
+ });
323
+
324
+ // Handle QR code
325
+ auth.on("qr", (dataUrl) => {
326
+ container.innerHTML = `
327
+ <img src="${dataUrl}" alt="Scan to login" />
328
+ <p>Scan with Pelican Vault app</p>
329
+ `;
330
+ });
331
+
332
+ // Handle deep link
333
+ auth.on("deeplink", (url) => {
334
+ container.innerHTML = `
335
+ <a href="${url}" class="btn">Open Pelican App</a>
336
+ `;
337
+ });
338
+
339
+ // Handle success
340
+ auth.on("success", (identity) => {
341
+ console.log("Logged in as:", identity.user_id);
342
+ // Redirect or update UI
343
+ window.location.href = "/dashboard";
344
+ });
345
+
346
+ // Handle errors
347
+ auth.on("error", (error) => {
348
+ container.innerHTML = `
349
+ <p class="error">${error.message}</p>
350
+ <button onclick="auth.start()">Retry</button>
351
+ `;
352
+ });
353
+
354
+ // Start authentication
355
+ auth.start();
356
+
357
+ // Cleanup on page unload
358
+ window.addEventListener("beforeunload", () => {
359
+ auth.destroy();
360
+ });
361
+ ```
362
+
363
+ ## Advanced Features
364
+
365
+ ### Continuous Mode
366
+
367
+ Automatically restart authentication sessions after completion. Useful for kiosks or multi-user scenarios.
368
+
369
+ ```typescript
370
+ const auth = new PelicanAuth({
371
+ publicKey: "your-key",
372
+ projectId: "your-project",
373
+ authType: "login",
374
+ continuousMode: true, // Enable continuous mode
375
+ });
376
+
377
+ auth.on("success", (identity) => {
378
+ // Process user
379
+ console.log("User authenticated:", identity);
380
+ // Session will automatically restart after ~300ms
381
+ });
382
+
383
+ auth.start();
384
+ ```
385
+
386
+ ### Force QR Code on Mobile
387
+
388
+ By default, mobile devices use deep links. Force QR code display instead:
389
+
390
+ ```typescript
391
+ const auth = new PelicanAuth({
392
+ publicKey: "your-key",
393
+ projectId: "your-project",
394
+ authType: "login",
395
+ forceQRCode: true, // Show QR even on mobile
396
+ });
397
+ ```
398
+
399
+ ### Session Recovery
400
+
401
+ The SDK automatically handles session recovery when users return from the Pelican Vault app:
402
+
403
+ ```typescript
404
+ // Session is automatically cached when deep link is opened
405
+ auth.on("deeplink", (url) => {
406
+ // User clicks and goes to Pelican app
407
+ window.location.href = url;
408
+ });
409
+
410
+ // When user returns (visibility change), session is recovered automatically
411
+ // Success event will fire if authentication completed
412
+ auth.on("success", (identity) => {
413
+ console.log("Recovered session:", identity);
414
+ });
415
+ ```
416
+
417
+ ### State Machine Tracking
418
+
419
+ Track the complete authentication lifecycle:
420
+
421
+ ```typescript
422
+ auth.on("state", (state) => {
423
+ switch (state) {
424
+ case "idle":
425
+ console.log("Ready to start");
426
+ break;
427
+ case "initializing":
428
+ console.log("Connecting to server...");
429
+ break;
430
+ case "awaiting-pair":
431
+ console.log("Waiting for device...");
432
+ break;
433
+ case "paired":
434
+ console.log("Device connected!");
435
+ break;
436
+ case "authenticated":
437
+ console.log("User authenticated!");
438
+ break;
439
+ case "confirmed":
440
+ console.log("Session confirmed");
441
+ break;
442
+ case "error":
443
+ console.log("Error occurred");
444
+ break;
445
+ }
446
+ });
447
+ ```
448
+
449
+ ## Error Handling
450
+
451
+ The SDK provides detailed error information:
452
+
453
+ ```typescript
454
+ auth.on("error", (error) => {
455
+ console.error("Error type:", error.message);
456
+
457
+ // Common errors:
458
+ // - "Failed to fetch relay URL" - Server connection issue
459
+ // - "WebSocket connection failed" - Network issue
460
+ // - "Invalid authentication payload" - Data corruption
461
+ // - "Failed to decrypt authentication data" - Crypto error
462
+ // - "Authenticating device terminated the connection" - User cancelled
463
+ // - "Session recovery failed" - Invalid cached session
464
+ });
465
+ ```
466
+
467
+ ## Security Considerations
468
+
469
+ 1. **Public Key and Project ID**: It is recommended to use environment variables or fetch from a secure server for production.
470
+ 2. **HTTPS Required**: Always use HTTPS in production
471
+ 3. **Session Expiry**: Sessions expire after 5 minutes by default
472
+ 4. **End-to-End Encryption**: All data is encrypted using TweetNaCl (NaCl crypto)
473
+ 5. **No Password Storage**: Authentication happens entirely through the Pelican Vault app
474
+
475
+ ## Browser Support
476
+
477
+ - Chrome/Edge 90+
478
+ - Firefox 88+
479
+ - Safari 14+
480
+ - Opera 76+
481
+
482
+ **Required APIs:**
483
+
484
+ - WebSocket
485
+ - Crypto API (`crypto.randomUUID()`)
486
+ - sessionStorage
487
+ - Visibility API
488
+
489
+ ## Troubleshooting
490
+
491
+ ### QR Code Not Displaying
492
+
493
+ ```typescript
494
+ auth.on("qr", (dataUrl) => {
495
+ if (!dataUrl) {
496
+ console.error("QR code generation failed");
497
+ }
498
+ });
499
+ ```
500
+
501
+ ### WebSocket Connection Issues
502
+
503
+ ```typescript
504
+ // Check network connectivity
505
+ auth.on("error", (error) => {
506
+ if (error.message.includes("WebSocket")) {
507
+ console.log("Check network connection");
508
+ // Retry logic
509
+ setTimeout(() => auth.start(), 5000);
510
+ }
511
+ });
512
+ ```
513
+
514
+ ### Session Recovery Not Working
515
+
516
+ Ensure visibility change detection is working:
517
+
518
+ ```typescript
519
+ document.addEventListener("visibilitychange", () => {
520
+ console.log("Visibility changed:", document.visibilityState);
521
+ });
522
+ ```
523
+
524
+ ## TypeScript Support
525
+
526
+ Full TypeScript definitions included:
527
+
528
+ ```typescript
529
+ import type {
530
+ IEmail,
531
+ IKycData,
532
+ IPhone,
533
+ IUserData,
534
+ IdentityResult,
535
+ IdCardTypes,
536
+ } from "@pelican-identity/sdk";
537
+ import {
538
+ AuthType,
539
+ ISocketMessage,
540
+ PelicanAuthConfig,
541
+ PelicanAuthEventMap,
542
+ PelicanAuthState,
543
+ } from "@pelican-identity/sdk";
544
+
545
+ const config: PelicanAuthConfig = {
546
+ publicKey: "your-key",
547
+ projectId: "your-project",
548
+ authType: "login",
549
+ };
550
+
551
+ const auth = new PelicanAuth(config);
552
+
553
+ auth.on("success", (identity: IdentityResult) => {
554
+ const userId: string = identity.user_id;
555
+ });
556
+ ```
557
+
558
+ ## License
559
+
560
+ MIT
561
+
562
+ ## Support
563
+
564
+ - Documentation: https://docs.pelicanidentity.com
565
+ - Issues: https://github.com/pelican-identity/sdk/issues
566
+ - Email: support@pelicanidentity.com
567
+
568
+ ## Changelog
569
+
570
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pelican-identity/auth-core",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Framework-agnostic authentication SDK for Pelican Identity",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",