@liveauth-labs/sdk 0.1.2 → 0.2.1
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 +178 -49
- package/dist/index.cjs +165 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -6
- package/dist/index.d.ts +72 -6
- package/dist/index.js +159 -48
- package/dist/index.js.map +1 -1
- package/dist/pow.worker.cjs +48 -3
- package/dist/pow.worker.cjs.map +1 -1
- package/dist/pow.worker.d.cts +15 -1
- package/dist/pow.worker.d.ts +15 -1
- package/dist/pow.worker.js +33 -3
- package/dist/pow.worker.js.map +1 -1
- package/package.json +17 -5
package/README.md
CHANGED
|
@@ -1,48 +1,42 @@
|
|
|
1
|
-
# LiveAuth
|
|
1
|
+
# LiveAuth JS SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Human verification through economics, not heuristics.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@liveauth-labs/sdk)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## What is LiveAuth?
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
cryptographic proof. If that fails or is skipped, it falls back to a small
|
|
9
|
-
Bitcoin Lightning payment.
|
|
10
|
+
LiveAuth verifies humans economically instead of heuristically.
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
No fingerprinting.
|
|
13
|
-
No behavioral profiling.
|
|
12
|
+
Instead of CAPTCHAs or tracking, LiveAuth asks the browser to perform a short cryptographic proof. If that fails or is skipped, it falls back to a small Bitcoin Lightning payment.
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
- No cookies
|
|
15
|
+
- No fingerprinting
|
|
16
|
+
- No behavioral profiling
|
|
16
17
|
|
|
17
|
-
##
|
|
18
|
+
## How It Works
|
|
18
19
|
|
|
19
|
-
When a user
|
|
20
|
+
When a user triggers verification:
|
|
20
21
|
|
|
21
22
|
1. **Browser attempts Proof-of-Work (PoW)**
|
|
22
|
-
|
|
23
|
-
– Bots pay CPU / battery cost
|
|
23
|
+
Takes ~200–800ms for a real device. Bots pay CPU/battery cost.
|
|
24
24
|
|
|
25
25
|
2. **If PoW fails → Lightning fallback**
|
|
26
|
-
|
|
27
|
-
– Real economic cost to bots
|
|
26
|
+
Small payment (e.g. 21 sats). Real economic cost to bots.
|
|
28
27
|
|
|
29
28
|
3. **LiveAuth returns a short-lived JWT**
|
|
30
|
-
|
|
31
|
-
– No user identity required
|
|
29
|
+
Verifiable on your backend. No user identity required.
|
|
32
30
|
|
|
33
31
|
Most humans never see the Lightning step.
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
## Installation (JavaScript)
|
|
33
|
+
## Installation
|
|
38
34
|
|
|
39
35
|
```bash
|
|
40
36
|
npm install @liveauth-labs/sdk
|
|
41
37
|
```
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## Basic Usage
|
|
39
|
+
## Quick Start
|
|
46
40
|
|
|
47
41
|
```ts
|
|
48
42
|
import { LiveAuth } from '@liveauth-labs/sdk';
|
|
@@ -52,52 +46,187 @@ const liveauth = new LiveAuth({
|
|
|
52
46
|
});
|
|
53
47
|
|
|
54
48
|
const result = await liveauth.verify();
|
|
49
|
+
|
|
50
|
+
if (result.method === 'pow') {
|
|
51
|
+
// PoW succeeded - send token to your backend
|
|
52
|
+
console.log('Verified via PoW:', result.token);
|
|
53
|
+
} else {
|
|
54
|
+
// Lightning fallback - show invoice to user
|
|
55
|
+
console.log('Pay invoice:', result.lightning.invoice);
|
|
56
|
+
|
|
57
|
+
// Poll for payment confirmation
|
|
58
|
+
const token = await liveauth.pollLightning(result.lightning.sessionId);
|
|
59
|
+
console.log('Verified via Lightning:', token);
|
|
60
|
+
}
|
|
55
61
|
```
|
|
56
62
|
|
|
57
|
-
|
|
63
|
+
## API Reference
|
|
64
|
+
|
|
65
|
+
### `new LiveAuth(config)`
|
|
66
|
+
|
|
67
|
+
| Option | Type | Required | Description |
|
|
68
|
+
|--------|------|----------|-------------|
|
|
69
|
+
| `publicKey` | `string` | ✓ | Your LiveAuth public key |
|
|
70
|
+
| `baseUrl` | `string` | | API base URL (default: `https://api.liveauth.app`) |
|
|
71
|
+
|
|
72
|
+
### `verify(options?)`
|
|
73
|
+
|
|
74
|
+
Attempts verification, starting with PoW and falling back to Lightning if needed.
|
|
58
75
|
|
|
59
|
-
|
|
76
|
+
| Option | Type | Default | Description |
|
|
77
|
+
|--------|------|---------|-------------|
|
|
78
|
+
| `forceLightning` | `boolean` | `false` | Skip PoW, go straight to Lightning |
|
|
79
|
+
| `onProgress` | `function` | | Callback: `(hashesPerSec, iterations) => void` |
|
|
80
|
+
| `powTimeoutMs` | `number` | `30000` | Max time for PoW before fallback |
|
|
81
|
+
| `maxPowIterations` | `number` | `50000000` | Max iterations before fallback |
|
|
82
|
+
|
|
83
|
+
**Returns:** `Promise<LiveAuthResult>`
|
|
60
84
|
|
|
61
85
|
```ts
|
|
86
|
+
// PoW success
|
|
62
87
|
{
|
|
63
|
-
|
|
64
|
-
|
|
88
|
+
method: 'pow',
|
|
89
|
+
token: 'eyJhbGciOi...',
|
|
65
90
|
solveMs: 412,
|
|
66
91
|
difficultyBits: 18
|
|
67
92
|
}
|
|
93
|
+
|
|
94
|
+
// Lightning fallback
|
|
95
|
+
{
|
|
96
|
+
method: 'lightning',
|
|
97
|
+
lightning: {
|
|
98
|
+
sessionId: 'sess_xxx',
|
|
99
|
+
invoice: 'lnbc...',
|
|
100
|
+
amountSats: 21,
|
|
101
|
+
expiresAtUnix: 1234567890,
|
|
102
|
+
mode: 'LIVE'
|
|
103
|
+
},
|
|
104
|
+
diagnostics: {
|
|
105
|
+
reason: 'pow_unsupported'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
68
108
|
```
|
|
69
109
|
|
|
70
|
-
|
|
110
|
+
### `pollLightning(sessionId, options?)`
|
|
71
111
|
|
|
72
|
-
|
|
112
|
+
Polls for Lightning payment confirmation.
|
|
73
113
|
|
|
74
|
-
|
|
114
|
+
| Option | Type | Default | Description |
|
|
115
|
+
|--------|------|---------|-------------|
|
|
116
|
+
| `timeoutMs` | `number` | `300000` | Max wait time (5 min) |
|
|
117
|
+
| `intervalMs` | `number` | `2000` | Poll interval |
|
|
118
|
+
| `signal` | `AbortSignal` | | Cancellation signal |
|
|
75
119
|
|
|
76
|
-
The token
|
|
120
|
+
**Returns:** `Promise<string>` - The verification token
|
|
77
121
|
|
|
78
|
-
|
|
79
|
-
- projectPublicKey
|
|
80
|
-
- authType (pow or lightning)
|
|
81
|
-
- short expiration (default: 10 minutes)
|
|
122
|
+
## Error Handling
|
|
82
123
|
|
|
83
|
-
|
|
124
|
+
```ts
|
|
125
|
+
import {
|
|
126
|
+
LiveAuth,
|
|
127
|
+
LiveAuthTimeoutError,
|
|
128
|
+
LiveAuthCancelledError,
|
|
129
|
+
LiveAuthNetworkError,
|
|
130
|
+
LiveAuthPowTimeoutError
|
|
131
|
+
} from '@liveauth-labs/sdk';
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const result = await liveauth.verify();
|
|
135
|
+
} catch (err) {
|
|
136
|
+
if (err instanceof LiveAuthNetworkError) {
|
|
137
|
+
console.error('Network issue:', err.message);
|
|
138
|
+
} else if (err instanceof LiveAuthPowTimeoutError) {
|
|
139
|
+
console.error('PoW took too long');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
84
143
|
|
|
85
|
-
##
|
|
144
|
+
## Progress Feedback
|
|
86
145
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
146
|
+
Show users what's happening during PoW:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const result = await liveauth.verify({
|
|
150
|
+
onProgress: (hashesPerSec, iterations) => {
|
|
151
|
+
console.log(`${(iterations / 1000).toFixed(0)}k hashes @ ${hashesPerSec}/sec`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
```
|
|
93
155
|
|
|
94
|
-
|
|
156
|
+
## Backend Verification
|
|
157
|
+
|
|
158
|
+
Send the JWT to your backend and verify it using your LiveAuth secret key.
|
|
159
|
+
|
|
160
|
+
### Example (Node.js)
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import jwt from 'jsonwebtoken';
|
|
164
|
+
|
|
165
|
+
app.post('/api/verify-liveauth', (req, res) => {
|
|
166
|
+
const { token } = req.body;
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
// Verify JWT signature with your LiveAuth secret key
|
|
170
|
+
const decoded = jwt.verify(token, process.env.LIVEAUTH_SECRET_KEY);
|
|
171
|
+
|
|
172
|
+
// Check expiration (JWT library handles this, but you can also check manually)
|
|
173
|
+
if (decoded.exp && decoded.exp < Date.now() / 1000) {
|
|
174
|
+
return res.status(401).json({ error: 'Token expired' });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Extract claims
|
|
178
|
+
const { projectId, projectPublicKey, authType, sub } = decoded;
|
|
179
|
+
|
|
180
|
+
// Verify it matches your project
|
|
181
|
+
if (projectPublicKey !== process.env.LIVEAUTH_PUBLIC_KEY) {
|
|
182
|
+
return res.status(401).json({ error: 'Invalid project' });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Success - user is verified
|
|
186
|
+
res.json({
|
|
187
|
+
verified: true,
|
|
188
|
+
authType, // 'pow' or 'lightning'
|
|
189
|
+
userId: sub
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
} catch (err) {
|
|
193
|
+
res.status(401).json({ error: 'Invalid token' });
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Token Claims
|
|
199
|
+
|
|
200
|
+
The JWT contains:
|
|
201
|
+
- `sub` - Unique user identifier (format: `pow:{projectId}:{challengeHex}` or `lightning:{invoiceId}`)
|
|
202
|
+
- `projectId` - Your project's UUID
|
|
203
|
+
- `projectPublicKey` - Your public API key
|
|
204
|
+
- `authType` - Verification method: `pow` or `lightning`
|
|
205
|
+
- `exp` - Expiration timestamp (default: 10 minutes from issuance)
|
|
206
|
+
- `iat` - Issued at timestamp
|
|
207
|
+
|
|
208
|
+
**Security Notes:**
|
|
209
|
+
- Always verify the JWT signature using your secret key
|
|
210
|
+
- Check the `projectPublicKey` claim matches your expected key
|
|
211
|
+
- Respect the `exp` (expiration) claim
|
|
212
|
+
- The `sub` claim is ephemeral - don't use it as a permanent user ID unless you're tracking sessions
|
|
95
213
|
|
|
96
214
|
## Debug Mode
|
|
97
215
|
|
|
98
216
|
Add `?liveauth_debug=1` to your URL to see:
|
|
217
|
+
- Verification method used
|
|
218
|
+
- PoW difficulty and solve time
|
|
219
|
+
- Lightning fallback triggers
|
|
220
|
+
|
|
221
|
+
## Why LiveAuth?
|
|
222
|
+
|
|
223
|
+
| Traditional CAPTCHA | LiveAuth |
|
|
224
|
+
|--------------------|----------|
|
|
225
|
+
| Tracks behavior | No tracking |
|
|
226
|
+
| ML heuristics | Cryptography |
|
|
227
|
+
| CAPTCHA farms | Real economic cost |
|
|
228
|
+
| Bad UX | Invisible to most humans |
|
|
229
|
+
|
|
230
|
+
## License
|
|
99
231
|
|
|
100
|
-
|
|
101
|
-
- PoW difficulty
|
|
102
|
-
- Solve time
|
|
103
|
-
- Lightning fallback (demo mode)
|
|
232
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -20,26 +20,51 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
LiveAuth: () => LiveAuth
|
|
23
|
+
LiveAuth: () => LiveAuth,
|
|
24
|
+
LiveAuthCancelledError: () => LiveAuthCancelledError,
|
|
25
|
+
LiveAuthNetworkError: () => LiveAuthNetworkError,
|
|
26
|
+
LiveAuthPowTimeoutError: () => LiveAuthPowTimeoutError,
|
|
27
|
+
LiveAuthPowUnsupportedError: () => LiveAuthPowUnsupportedError,
|
|
28
|
+
LiveAuthTimeoutError: () => LiveAuthTimeoutError
|
|
24
29
|
});
|
|
25
30
|
module.exports = __toCommonJS(src_exports);
|
|
26
31
|
|
|
27
32
|
// src/errors.ts
|
|
28
33
|
var LiveAuthTimeoutError = class extends Error {
|
|
29
|
-
constructor(message = "
|
|
34
|
+
constructor(message = "LiveAuth verification timed out") {
|
|
30
35
|
super(message);
|
|
31
36
|
this.name = "LiveAuthTimeoutError";
|
|
32
37
|
}
|
|
33
38
|
};
|
|
34
39
|
var LiveAuthCancelledError = class extends Error {
|
|
35
|
-
constructor(message = "
|
|
40
|
+
constructor(message = "LiveAuth verification cancelled") {
|
|
36
41
|
super(message);
|
|
37
42
|
this.name = "LiveAuthCancelledError";
|
|
38
43
|
}
|
|
39
44
|
};
|
|
45
|
+
var LiveAuthNetworkError = class extends Error {
|
|
46
|
+
constructor(message = "LiveAuth network request failed", cause) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.cause = cause;
|
|
49
|
+
this.name = "LiveAuthNetworkError";
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var LiveAuthPowUnsupportedError = class extends Error {
|
|
53
|
+
constructor(message = "Proof-of-Work is not supported in this environment") {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "LiveAuthPowUnsupportedError";
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var LiveAuthPowTimeoutError = class extends Error {
|
|
59
|
+
constructor(message = "Proof-of-Work timed out") {
|
|
60
|
+
super(message);
|
|
61
|
+
this.name = "LiveAuthPowTimeoutError";
|
|
62
|
+
}
|
|
63
|
+
};
|
|
40
64
|
|
|
41
65
|
// src/index.ts
|
|
42
66
|
var import_meta = {};
|
|
67
|
+
var SDK_VERSION = "0.2.0";
|
|
43
68
|
var LiveAuth = class {
|
|
44
69
|
constructor(config) {
|
|
45
70
|
this.config = config;
|
|
@@ -49,85 +74,149 @@ var LiveAuth = class {
|
|
|
49
74
|
this.baseUrl = config.baseUrl ?? "https://api.liveauth.app";
|
|
50
75
|
this.headers = {
|
|
51
76
|
"Content-Type": "application/json",
|
|
52
|
-
"X-LW-Public": config.publicKey
|
|
77
|
+
"X-LW-Public": config.publicKey,
|
|
78
|
+
"X-LW-SDK-Version": SDK_VERSION
|
|
53
79
|
};
|
|
54
80
|
}
|
|
55
81
|
/* ======================================================
|
|
56
82
|
* PUBLIC ENTRYPOINT
|
|
57
83
|
* ====================================================== */
|
|
58
|
-
async verify() {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
sig: challenge.sig
|
|
68
|
-
});
|
|
69
|
-
if (verifyRes.verified && verifyRes.token) {
|
|
84
|
+
async verify(options = {}) {
|
|
85
|
+
const {
|
|
86
|
+
forceLightning = false,
|
|
87
|
+
onProgress,
|
|
88
|
+
powTimeoutMs = 3e4,
|
|
89
|
+
maxPowIterations = 5e7
|
|
90
|
+
} = options;
|
|
91
|
+
if (forceLightning) {
|
|
92
|
+
const lightning = await this.startLightning();
|
|
70
93
|
return {
|
|
71
|
-
method: "
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
difficultyBits: challenge.difficultyBits
|
|
94
|
+
method: "lightning",
|
|
95
|
+
lightning,
|
|
96
|
+
diagnostics: { reason: "forced_lightning" }
|
|
75
97
|
};
|
|
76
98
|
}
|
|
77
|
-
if (
|
|
99
|
+
if (!this.canUsePow()) {
|
|
78
100
|
const lightning = await this.startLightning();
|
|
79
101
|
return {
|
|
80
102
|
method: "lightning",
|
|
81
|
-
lightning
|
|
103
|
+
lightning,
|
|
104
|
+
diagnostics: { reason: "pow_unsupported" }
|
|
82
105
|
};
|
|
83
106
|
}
|
|
84
|
-
|
|
107
|
+
const startedAt = performance.now();
|
|
108
|
+
try {
|
|
109
|
+
const challenge = await this.getPowChallenge();
|
|
110
|
+
const solution = await this.solvePow(challenge, {
|
|
111
|
+
onProgress,
|
|
112
|
+
timeoutMs: powTimeoutMs,
|
|
113
|
+
maxIterations: maxPowIterations
|
|
114
|
+
});
|
|
115
|
+
const verifyRes = await this.verifyPow({
|
|
116
|
+
challengeHex: challenge.challengeHex,
|
|
117
|
+
nonce: solution.nonce,
|
|
118
|
+
hashHex: solution.hashHex,
|
|
119
|
+
expiresAtUnix: challenge.expiresAtUnix,
|
|
120
|
+
difficultyBits: challenge.difficultyBits,
|
|
121
|
+
sig: challenge.sig
|
|
122
|
+
});
|
|
123
|
+
if (verifyRes.verified && verifyRes.token) {
|
|
124
|
+
return {
|
|
125
|
+
method: "pow",
|
|
126
|
+
token: verifyRes.token,
|
|
127
|
+
solveMs: Math.round(performance.now() - startedAt),
|
|
128
|
+
difficultyBits: challenge.difficultyBits
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (verifyRes.fallback === "lightning") {
|
|
132
|
+
const lightning = await this.startLightning();
|
|
133
|
+
return {
|
|
134
|
+
method: "lightning",
|
|
135
|
+
lightning,
|
|
136
|
+
diagnostics: { reason: "pow_server_fallback" }
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
throw new Error("LiveAuth: verification failed");
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof LiveAuthPowTimeoutError || err instanceof LiveAuthPowUnsupportedError) {
|
|
142
|
+
const lightning = await this.startLightning();
|
|
143
|
+
return {
|
|
144
|
+
method: "lightning",
|
|
145
|
+
lightning,
|
|
146
|
+
diagnostics: {
|
|
147
|
+
reason: "pow_failed",
|
|
148
|
+
detail: err.message
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
85
154
|
}
|
|
86
155
|
/* ======================================================
|
|
87
156
|
* POW FLOW
|
|
88
157
|
* ====================================================== */
|
|
89
158
|
async getPowChallenge() {
|
|
90
|
-
const res = await
|
|
159
|
+
const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/pow/challenge`, {
|
|
91
160
|
headers: this.headers
|
|
92
161
|
});
|
|
93
|
-
if (!res.ok) throw new
|
|
162
|
+
if (!res.ok) throw new LiveAuthNetworkError("PoW challenge failed");
|
|
94
163
|
return res.json();
|
|
95
164
|
}
|
|
96
165
|
async verifyPow(req) {
|
|
97
|
-
const res = await
|
|
166
|
+
const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/pow/verify`, {
|
|
98
167
|
method: "POST",
|
|
99
168
|
headers: this.headers,
|
|
100
169
|
body: JSON.stringify(req)
|
|
101
170
|
});
|
|
102
|
-
if (!res.ok) throw new
|
|
171
|
+
if (!res.ok) throw new LiveAuthNetworkError("PoW verify failed");
|
|
103
172
|
return res.json();
|
|
104
173
|
}
|
|
105
174
|
/* ======================================================
|
|
106
175
|
* POW SOLVER (WORKER)
|
|
107
176
|
* ====================================================== */
|
|
108
|
-
solvePow(challenge) {
|
|
177
|
+
solvePow(challenge, options = {}) {
|
|
178
|
+
const { onProgress, timeoutMs = 3e4, maxIterations = 5e7 } = options;
|
|
109
179
|
if (!this.canUsePow()) {
|
|
110
|
-
return Promise.reject(
|
|
111
|
-
new Error("LiveAuth: PoW not supported in this environment")
|
|
112
|
-
);
|
|
180
|
+
return Promise.reject(new LiveAuthPowUnsupportedError());
|
|
113
181
|
}
|
|
114
182
|
return new Promise((resolve, reject) => {
|
|
115
183
|
const worker = new Worker(
|
|
116
184
|
new URL("./pow.worker.js", import_meta.url),
|
|
117
185
|
{ type: "module" }
|
|
118
186
|
);
|
|
119
|
-
|
|
187
|
+
const timeoutId = setTimeout(() => {
|
|
120
188
|
worker.terminate();
|
|
121
|
-
|
|
189
|
+
reject(new LiveAuthPowTimeoutError(`PoW timed out after ${timeoutMs}ms`));
|
|
190
|
+
}, timeoutMs);
|
|
191
|
+
worker.onmessage = (e) => {
|
|
192
|
+
const data = e.data;
|
|
193
|
+
if (data.type === "progress") {
|
|
194
|
+
onProgress?.(data.hashesPerSec ?? 0, data.iterations ?? 0);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (data.type === "timeout") {
|
|
198
|
+
clearTimeout(timeoutId);
|
|
199
|
+
worker.terminate();
|
|
200
|
+
reject(new LiveAuthPowTimeoutError(`PoW hit max iterations (${maxIterations})`));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (data.type === "solution" && data.nonce !== void 0 && data.hashHex) {
|
|
204
|
+
clearTimeout(timeoutId);
|
|
205
|
+
worker.terminate();
|
|
206
|
+
resolve({ nonce: data.nonce, hashHex: data.hashHex });
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
122
209
|
};
|
|
123
210
|
worker.onerror = (e) => {
|
|
211
|
+
clearTimeout(timeoutId);
|
|
124
212
|
worker.terminate();
|
|
125
|
-
reject(e);
|
|
213
|
+
reject(new LiveAuthPowUnsupportedError(`Worker error: ${e.message}`));
|
|
126
214
|
};
|
|
127
215
|
worker.postMessage({
|
|
128
216
|
projectPublicKey: challenge.projectPublicKey,
|
|
129
217
|
challengeHex: challenge.challengeHex,
|
|
130
|
-
targetHex: challenge.targetHex
|
|
218
|
+
targetHex: challenge.targetHex,
|
|
219
|
+
maxIterations
|
|
131
220
|
});
|
|
132
221
|
});
|
|
133
222
|
}
|
|
@@ -135,26 +224,16 @@ var LiveAuth = class {
|
|
|
135
224
|
* LIGHTNING FALLBACK
|
|
136
225
|
* ====================================================== */
|
|
137
226
|
async startLightning() {
|
|
138
|
-
const res = await
|
|
227
|
+
const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/auth/start`, {
|
|
139
228
|
method: "POST",
|
|
140
229
|
headers: this.headers,
|
|
141
230
|
body: JSON.stringify({ userHint: "browser" })
|
|
142
231
|
});
|
|
143
232
|
if (!res.ok) {
|
|
144
|
-
throw new
|
|
233
|
+
throw new LiveAuthNetworkError("Lightning auth start failed");
|
|
145
234
|
}
|
|
146
235
|
return res.json();
|
|
147
236
|
}
|
|
148
|
-
canUsePow() {
|
|
149
|
-
try {
|
|
150
|
-
if (typeof window === "undefined") return false;
|
|
151
|
-
if (typeof Worker === "undefined") return false;
|
|
152
|
-
if (typeof URL === "undefined") return false;
|
|
153
|
-
return true;
|
|
154
|
-
} catch {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
237
|
async pollLightning(sessionId, options) {
|
|
159
238
|
const timeoutMs = options?.timeoutMs ?? 5 * 6e4;
|
|
160
239
|
const intervalMs = options?.intervalMs ?? 2e3;
|
|
@@ -187,7 +266,7 @@ var LiveAuth = class {
|
|
|
187
266
|
}
|
|
188
267
|
);
|
|
189
268
|
if (!res.ok) {
|
|
190
|
-
throw new
|
|
269
|
+
throw new LiveAuthNetworkError("Lightning confirm failed");
|
|
191
270
|
}
|
|
192
271
|
const json = await res.json();
|
|
193
272
|
if (json.verified && json.token) {
|
|
@@ -199,10 +278,47 @@ var LiveAuth = class {
|
|
|
199
278
|
clearTimeout(timeoutId);
|
|
200
279
|
}
|
|
201
280
|
}
|
|
281
|
+
/* ======================================================
|
|
282
|
+
* UTILITIES
|
|
283
|
+
* ====================================================== */
|
|
284
|
+
canUsePow() {
|
|
285
|
+
try {
|
|
286
|
+
if (typeof window === "undefined") return false;
|
|
287
|
+
if (typeof Worker === "undefined") return false;
|
|
288
|
+
if (typeof URL === "undefined") return false;
|
|
289
|
+
if (typeof crypto === "undefined" || !crypto.subtle) return false;
|
|
290
|
+
return true;
|
|
291
|
+
} catch {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
async fetchWithRetry(url, init, retries = 2, backoffMs = 500) {
|
|
296
|
+
let lastError;
|
|
297
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
298
|
+
try {
|
|
299
|
+
const res = await fetch(url, init);
|
|
300
|
+
return res;
|
|
301
|
+
} catch (err) {
|
|
302
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
303
|
+
if (attempt < retries) {
|
|
304
|
+
await sleep(backoffMs * Math.pow(2, attempt));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
throw new LiveAuthNetworkError(
|
|
309
|
+
`Request failed after ${retries + 1} attempts`,
|
|
310
|
+
lastError
|
|
311
|
+
);
|
|
312
|
+
}
|
|
202
313
|
};
|
|
203
314
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
204
315
|
// Annotate the CommonJS export names for ESM import in node:
|
|
205
316
|
0 && (module.exports = {
|
|
206
|
-
LiveAuth
|
|
317
|
+
LiveAuth,
|
|
318
|
+
LiveAuthCancelledError,
|
|
319
|
+
LiveAuthNetworkError,
|
|
320
|
+
LiveAuthPowTimeoutError,
|
|
321
|
+
LiveAuthPowUnsupportedError,
|
|
322
|
+
LiveAuthTimeoutError
|
|
207
323
|
});
|
|
208
324
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts"],"sourcesContent":["import type {\n LiveAuthResult,\n VerifyOptions,\n PowChallengeResponse,\n PowVerifyRequest,\n PowVerifyResponse,\n PowSolution,\n AuthStartResponse,\n AuthConfirmResponse,\n LiveAuthConfig, LightningStart\n} from './types';\nimport {LiveAuthCancelledError, LiveAuthTimeoutError} from \"./errors\";\n\nexport class LiveAuth {\n private readonly baseUrl: string;\n private readonly headers: HeadersInit;\n\n constructor(private readonly config: LiveAuthConfig) {\n if (!config.publicKey) {\n throw new Error('LiveAuth: publicKey is required');\n }\n\n this.baseUrl = config.baseUrl ?? 'https://api.liveauth.app';\n\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-LW-Public': config.publicKey\n };\n }\n\n /* ======================================================\n * PUBLIC ENTRYPOINT\n * ====================================================== */\n\n async verify(): Promise<LiveAuthResult> {\n const startedAt = performance.now();\n\n const challenge = await this.getPowChallenge();\n const solution = await this.solvePow(challenge);\n\n const verifyRes = await this.verifyPow({\n challengeHex: challenge.challengeHex,\n nonce: solution.nonce,\n hashHex: solution.hashHex,\n expiresAtUnix: challenge.expiresAtUnix,\n sig: challenge.sig\n });\n\n if (verifyRes.verified && verifyRes.token) {\n return {\n method: 'pow',\n token: verifyRes.token,\n solveMs: Math.round(performance.now() - startedAt),\n difficultyBits: challenge.difficultyBits\n };\n }\n\n if (verifyRes.fallback === 'lightning') {\n const lightning = await this.startLightning();\n return {\n method: 'lightning',\n lightning\n };\n }\n\n throw new Error('LiveAuth: verification failed');\n }\n\n /* ======================================================\n * POW FLOW\n * ====================================================== */\n\n private async getPowChallenge(): Promise<PowChallengeResponse> {\n const res = await fetch(`${this.baseUrl}/api/public/pow/challenge`, {\n headers: this.headers\n });\n\n if (!res.ok) throw new Error('PoW challenge failed');\n return res.json();\n }\n\n private async verifyPow(req: PowVerifyRequest): Promise<PowVerifyResponse> {\n const res = await fetch(`${this.baseUrl}/api/public/pow/verify`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(req)\n });\n\n if (!res.ok) throw new Error('PoW verify failed');\n return res.json();\n }\n\n /* ======================================================\n * POW SOLVER (WORKER)\n * ====================================================== */\n\n private solvePow(challenge: PowChallengeResponse): Promise<PowSolution> {\n if (!this.canUsePow()) {\n return Promise.reject(\n new Error('LiveAuth: PoW not supported in this environment')\n );\n }\n\n return new Promise((resolve, reject) => {\n const worker = new Worker(\n new URL('./pow.worker.js', import.meta.url),\n {type: 'module'}\n );\n\n worker.onmessage = e => {\n worker.terminate();\n resolve(e.data);\n };\n\n worker.onerror = e => {\n worker.terminate();\n reject(e);\n };\n\n worker.postMessage({\n projectPublicKey: challenge.projectPublicKey,\n challengeHex: challenge.challengeHex,\n targetHex: challenge.targetHex\n });\n });\n }\n\n /* ======================================================\n * LIGHTNING FALLBACK\n * ====================================================== */\n\n private async startLightning(): Promise<LightningStart> {\n const res = await fetch(`${this.baseUrl}/api/public/auth/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({userHint: 'browser'})\n });\n\n if (!res.ok) {\n throw new Error('Lightning auth start failed');\n }\n\n return res.json();\n }\n\n private canUsePow(): boolean {\n try {\n // SSR / Node guard\n if (typeof window === 'undefined') return false;\n\n // Worker support\n if (typeof Worker === 'undefined') return false;\n\n // Basic URL support (needed for module workers)\n if (typeof URL === 'undefined') return false;\n\n return true;\n } catch {\n return false;\n }\n }\n\n async pollLightning(\n sessionId: string,\n options?: {\n timeoutMs?: number;\n signal?: AbortSignal;\n intervalMs?: number;\n }\n ): Promise<string> {\n const timeoutMs = options?.timeoutMs ?? 5 * 60_000; // 5 min\n const intervalMs = options?.intervalMs ?? 2000;\n const externalSignal = options?.signal;\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n // If caller passes a signal, bridge it\n if (externalSignal) {\n if (externalSignal.aborted) {\n throw new LiveAuthCancelledError();\n }\n\n externalSignal.addEventListener('abort', () => {\n controller.abort();\n });\n }\n\n // Timeout enforcement\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, timeoutMs);\n\n try {\n while (true) {\n if (signal.aborted) {\n throw externalSignal?.aborted\n ? new LiveAuthCancelledError()\n : new LiveAuthTimeoutError();\n }\n\n const res = await fetch(\n `${this.baseUrl}/api/public/auth/confirm`,\n {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({sessionId}),\n signal\n }\n );\n\n if (!res.ok) {\n throw new Error('Lightning confirm failed');\n }\n\n const json = await res.json();\n\n if (json.verified && json.token) {\n return json.token;\n }\n\n await sleep(intervalMs);\n }\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n\n}\n\n/* ======================================================\n * UTILS\n * ====================================================== */\n\nconst sleep = (ms: number) =>\n new Promise(resolve => setTimeout(resolve, ms));\n","export class LiveAuthTimeoutError extends Error {\n constructor(message = 'Lightning verification timed out') {\n super(message);\n this.name = 'LiveAuthTimeoutError';\n }\n}\n\nexport class LiveAuthCancelledError extends Error {\n constructor(message = 'Lightning verification cancelled') {\n super(message);\n this.name = 'LiveAuthCancelledError';\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts"],"sourcesContent":["import type {\n LiveAuthResult,\n VerifyOptions,\n PowChallengeResponse,\n PowVerifyRequest,\n PowVerifyResponse,\n PowSolution,\n LiveAuthConfig,\n LightningStart\n} from './types';\nimport {\n LiveAuthCancelledError,\n LiveAuthTimeoutError,\n LiveAuthNetworkError,\n LiveAuthPowUnsupportedError,\n LiveAuthPowTimeoutError\n} from './errors';\nimport type { PowWorkerResult } from './pow.worker';\n\n// Re-export for consumers\nexport * from './types';\nexport * from './errors';\n\nconst SDK_VERSION = '0.2.0';\n\nexport class LiveAuth {\n private readonly baseUrl: string;\n private readonly headers: HeadersInit;\n\n constructor(private readonly config: LiveAuthConfig) {\n if (!config.publicKey) {\n throw new Error('LiveAuth: publicKey is required');\n }\n\n this.baseUrl = config.baseUrl ?? 'https://api.liveauth.app';\n\n this.headers = {\n 'Content-Type': 'application/json',\n 'X-LW-Public': config.publicKey,\n 'X-LW-SDK-Version': SDK_VERSION\n };\n }\n\n /* ======================================================\n * PUBLIC ENTRYPOINT\n * ====================================================== */\n\n async verify(options: VerifyOptions = {}): Promise<LiveAuthResult> {\n const { \n forceLightning = false, \n onProgress,\n powTimeoutMs = 30_000,\n maxPowIterations = 50_000_000\n } = options;\n\n // Skip PoW if forced to Lightning or PoW not supported\n if (forceLightning) {\n const lightning = await this.startLightning();\n return {\n method: 'lightning',\n lightning,\n diagnostics: { reason: 'forced_lightning' }\n };\n }\n\n if (!this.canUsePow()) {\n const lightning = await this.startLightning();\n return {\n method: 'lightning',\n lightning,\n diagnostics: { reason: 'pow_unsupported' }\n };\n }\n\n const startedAt = performance.now();\n\n try {\n const challenge = await this.getPowChallenge();\n const solution = await this.solvePow(challenge, { \n onProgress, \n timeoutMs: powTimeoutMs,\n maxIterations: maxPowIterations\n });\n\n const verifyRes = await this.verifyPow({\n challengeHex: challenge.challengeHex,\n nonce: solution.nonce,\n hashHex: solution.hashHex,\n expiresAtUnix: challenge.expiresAtUnix,\n difficultyBits: challenge.difficultyBits,\n sig: challenge.sig\n });\n\n if (verifyRes.verified && verifyRes.token) {\n return {\n method: 'pow',\n token: verifyRes.token,\n solveMs: Math.round(performance.now() - startedAt),\n difficultyBits: challenge.difficultyBits\n };\n }\n\n if (verifyRes.fallback === 'lightning') {\n const lightning = await this.startLightning();\n return {\n method: 'lightning',\n lightning,\n diagnostics: { reason: 'pow_server_fallback' }\n };\n }\n\n throw new Error('LiveAuth: verification failed');\n\n } catch (err) {\n // On PoW failure, fall back to Lightning\n if (err instanceof LiveAuthPowTimeoutError || err instanceof LiveAuthPowUnsupportedError) {\n const lightning = await this.startLightning();\n return {\n method: 'lightning',\n lightning,\n diagnostics: { \n reason: 'pow_failed', \n detail: err.message \n }\n };\n }\n throw err;\n }\n }\n\n /* ======================================================\n * POW FLOW\n * ====================================================== */\n\n private async getPowChallenge(): Promise<PowChallengeResponse> {\n const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/pow/challenge`, {\n headers: this.headers\n });\n\n if (!res.ok) throw new LiveAuthNetworkError('PoW challenge failed');\n return res.json();\n }\n\n private async verifyPow(req: PowVerifyRequest): Promise<PowVerifyResponse> {\n const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/pow/verify`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify(req)\n });\n\n if (!res.ok) throw new LiveAuthNetworkError('PoW verify failed');\n return res.json();\n }\n\n /* ======================================================\n * POW SOLVER (WORKER)\n * ====================================================== */\n\n private solvePow(\n challenge: PowChallengeResponse, \n options: {\n onProgress?: (hashesPerSec: number, iterations: number) => void;\n timeoutMs?: number;\n maxIterations?: number;\n } = {}\n ): Promise<PowSolution> {\n const { onProgress, timeoutMs = 30_000, maxIterations = 50_000_000 } = options;\n\n if (!this.canUsePow()) {\n return Promise.reject(new LiveAuthPowUnsupportedError());\n }\n\n return new Promise((resolve, reject) => {\n const worker = new Worker(\n new URL('./pow.worker.js', import.meta.url),\n { type: 'module' }\n );\n\n const timeoutId = setTimeout(() => {\n worker.terminate();\n reject(new LiveAuthPowTimeoutError(`PoW timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n\n worker.onmessage = (e: MessageEvent<PowWorkerResult>) => {\n const data = e.data;\n\n if (data.type === 'progress') {\n onProgress?.(data.hashesPerSec ?? 0, data.iterations ?? 0);\n return;\n }\n\n if (data.type === 'timeout') {\n clearTimeout(timeoutId);\n worker.terminate();\n reject(new LiveAuthPowTimeoutError(`PoW hit max iterations (${maxIterations})`));\n return;\n }\n\n if (data.type === 'solution' && data.nonce !== undefined && data.hashHex) {\n clearTimeout(timeoutId);\n worker.terminate();\n resolve({ nonce: data.nonce, hashHex: data.hashHex });\n return;\n }\n };\n\n worker.onerror = e => {\n clearTimeout(timeoutId);\n worker.terminate();\n reject(new LiveAuthPowUnsupportedError(`Worker error: ${e.message}`));\n };\n\n worker.postMessage({\n projectPublicKey: challenge.projectPublicKey,\n challengeHex: challenge.challengeHex,\n targetHex: challenge.targetHex,\n maxIterations\n });\n });\n }\n\n /* ======================================================\n * LIGHTNING FALLBACK\n * ====================================================== */\n\n private async startLightning(): Promise<LightningStart> {\n const res = await this.fetchWithRetry(`${this.baseUrl}/api/public/auth/start`, {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ userHint: 'browser' })\n });\n\n if (!res.ok) {\n throw new LiveAuthNetworkError('Lightning auth start failed');\n }\n\n return res.json();\n }\n\n async pollLightning(\n sessionId: string,\n options?: {\n timeoutMs?: number;\n signal?: AbortSignal;\n intervalMs?: number;\n }\n ): Promise<string> {\n const timeoutMs = options?.timeoutMs ?? 5 * 60_000; // 5 min\n const intervalMs = options?.intervalMs ?? 2000;\n const externalSignal = options?.signal;\n\n const controller = new AbortController();\n const signal = controller.signal;\n\n // If caller passes a signal, bridge it\n if (externalSignal) {\n if (externalSignal.aborted) {\n throw new LiveAuthCancelledError();\n }\n\n externalSignal.addEventListener('abort', () => {\n controller.abort();\n });\n }\n\n // Timeout enforcement\n const timeoutId = setTimeout(() => {\n controller.abort();\n }, timeoutMs);\n\n try {\n while (true) {\n if (signal.aborted) {\n throw externalSignal?.aborted\n ? new LiveAuthCancelledError()\n : new LiveAuthTimeoutError();\n }\n\n const res = await fetch(\n `${this.baseUrl}/api/public/auth/confirm`,\n {\n method: 'POST',\n headers: this.headers,\n body: JSON.stringify({ sessionId }),\n signal\n }\n );\n\n if (!res.ok) {\n throw new LiveAuthNetworkError('Lightning confirm failed');\n }\n\n const json = await res.json();\n\n if (json.verified && json.token) {\n return json.token;\n }\n\n await sleep(intervalMs);\n }\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n /* ======================================================\n * UTILITIES\n * ====================================================== */\n\n private canUsePow(): boolean {\n try {\n // SSR / Node guard\n if (typeof window === 'undefined') return false;\n\n // Worker support\n if (typeof Worker === 'undefined') return false;\n\n // Basic URL support (needed for module workers)\n if (typeof URL === 'undefined') return false;\n\n // Crypto.subtle required for SHA-256\n if (typeof crypto === 'undefined' || !crypto.subtle) return false;\n\n return true;\n } catch {\n return false;\n }\n }\n\n private async fetchWithRetry(\n url: string,\n init?: RequestInit,\n retries = 2,\n backoffMs = 500\n ): Promise<Response> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n const res = await fetch(url, init);\n return res;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n \n if (attempt < retries) {\n await sleep(backoffMs * Math.pow(2, attempt));\n }\n }\n }\n\n throw new LiveAuthNetworkError(\n `Request failed after ${retries + 1} attempts`,\n lastError\n );\n }\n}\n\n/* ======================================================\n * UTILS\n * ====================================================== */\n\nconst sleep = (ms: number) =>\n new Promise(resolve => setTimeout(resolve, ms));\n","export class LiveAuthTimeoutError extends Error {\n constructor(message = 'LiveAuth verification timed out') {\n super(message);\n this.name = 'LiveAuthTimeoutError';\n }\n}\n\nexport class LiveAuthCancelledError extends Error {\n constructor(message = 'LiveAuth verification cancelled') {\n super(message);\n this.name = 'LiveAuthCancelledError';\n }\n}\n\nexport class LiveAuthNetworkError extends Error {\n constructor(message = 'LiveAuth network request failed', public readonly cause?: Error) {\n super(message);\n this.name = 'LiveAuthNetworkError';\n }\n}\n\nexport class LiveAuthPowUnsupportedError extends Error {\n constructor(message = 'Proof-of-Work is not supported in this environment') {\n super(message);\n this.name = 'LiveAuthPowUnsupportedError';\n }\n}\n\nexport class LiveAuthPowTimeoutError extends Error {\n constructor(message = 'Proof-of-Work timed out') {\n super(message);\n this.name = 'LiveAuthPowTimeoutError';\n }\n}\n"],"mappings":"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","names":[]}
|