stellar-drive 1.2.23 → 1.2.26
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/dist/auth/deviceVerification.d.ts.map +1 -1
- package/dist/auth/deviceVerification.js +7 -1
- package/dist/auth/deviceVerification.js.map +1 -1
- package/dist/auth/loginGuard.d.ts +56 -21
- package/dist/auth/loginGuard.d.ts.map +1 -1
- package/dist/auth/loginGuard.js +186 -80
- package/dist/auth/loginGuard.js.map +1 -1
- package/dist/auth/offlineCredentials.d.ts.map +1 -1
- package/dist/auth/offlineCredentials.js +4 -1
- package/dist/auth/offlineCredentials.js.map +1 -1
- package/dist/auth/offlineSession.d.ts.map +1 -1
- package/dist/auth/offlineSession.js +4 -1
- package/dist/auth/offlineSession.js.map +1 -1
- package/dist/auth/singleUser.d.ts.map +1 -1
- package/dist/auth/singleUser.js +4 -3
- package/dist/auth/singleUser.js.map +1 -1
- package/dist/bin/install-pwa.d.ts.map +1 -1
- package/dist/bin/install-pwa.js +424 -19
- package/dist/bin/install-pwa.js.map +1 -1
- package/dist/deviceId.d.ts +40 -3
- package/dist/deviceId.d.ts.map +1 -1
- package/dist/deviceId.js +152 -10
- package/dist/deviceId.js.map +1 -1
- package/dist/entries/auth.d.ts +1 -1
- package/dist/entries/auth.d.ts.map +1 -1
- package/dist/entries/auth.js +1 -1
- package/dist/entries/auth.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/supabase/auth.js +2 -2
- package/dist/supabase/auth.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DemoBanner.svelte +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceVerification.d.ts","sourceRoot":"","sources":["../../src/auth/deviceVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuE9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAyBvC;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/C;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"deviceVerification.d.ts","sourceRoot":"","sources":["../../src/auth/deviceVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuE9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAyBvC;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS/C;AAMD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCtE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BtE;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBtE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAoBhF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAanE;AAMD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAoC7F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CA6DxD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAmB3F;AAMD;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
*/
|
|
55
55
|
import { getEngineConfig } from '../config';
|
|
56
56
|
import { supabase } from '../supabase/client';
|
|
57
|
-
import { getDeviceId } from '../deviceId';
|
|
57
|
+
import { getDeviceId, waitForDeviceId } from '../deviceId';
|
|
58
58
|
import { debugLog, debugWarn, debugError } from '../debug';
|
|
59
59
|
import { isDemoMode } from '../demo';
|
|
60
60
|
/** Default number of days a device remains trusted before requiring re-verification. */
|
|
@@ -225,6 +225,9 @@ export async function isDeviceTrusted(userId) {
|
|
|
225
225
|
if (isDemoMode())
|
|
226
226
|
return true;
|
|
227
227
|
try {
|
|
228
|
+
/* Ensure the IDB recovery attempt has completed so that a localStorage-
|
|
229
|
+
cleared device ID is restored before we look it up in trusted_devices. */
|
|
230
|
+
await waitForDeviceId();
|
|
228
231
|
const deviceId = getDeviceId();
|
|
229
232
|
const trustDays = getTrustDurationDays();
|
|
230
233
|
/* Calculate the cutoff date — devices not used within this window
|
|
@@ -427,6 +430,9 @@ export async function sendDeviceVerification(email) {
|
|
|
427
430
|
if (isDemoMode())
|
|
428
431
|
return { error: null };
|
|
429
432
|
try {
|
|
433
|
+
/* Ensure the IDB recovery attempt has completed so the device ID we
|
|
434
|
+
embed in user_metadata is the recovered UUID, not a fresh one. */
|
|
435
|
+
await waitForDeviceId();
|
|
430
436
|
/* Store the pending device info in user_metadata so the confirm page
|
|
431
437
|
can trust THIS device even if the link is opened on a different one.
|
|
432
438
|
This enables the cross-device verification pattern. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceVerification.js","sourceRoot":"","sources":["../../src/auth/deviceVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"deviceVerification.js","sourceRoot":"","sources":["../../src/auth/deviceVerification.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Login Guard -- Local Credential Pre-Check &
|
|
2
|
+
* @fileoverview Login Guard -- Local Credential Pre-Check & Lockout
|
|
3
3
|
*
|
|
4
4
|
* Minimizes Supabase auth API requests by verifying credentials locally first.
|
|
5
5
|
* Only calls Supabase when the local hash matches (correct password) or when no
|
|
6
|
-
* local hash exists
|
|
6
|
+
* local hash exists.
|
|
7
7
|
*
|
|
8
8
|
* Architecture:
|
|
9
|
-
* - Maintains **in-memory
|
|
10
|
-
*
|
|
9
|
+
* - Maintains **in-memory** state for fast local-hash failure counters
|
|
10
|
+
* (resets on page refresh by design).
|
|
11
|
+
* - Maintains **persistent** lockout state in IndexedDB (`singleUserConfig`
|
|
12
|
+
* table, key `'pin_lockout'`). This survives page refreshes and tab
|
|
13
|
+
* closes, preventing brute-force attacks that rely on reloading to reset
|
|
14
|
+
* counters.
|
|
11
15
|
* - Two operational strategies:
|
|
12
16
|
* 1. `local-match`: A cached hash exists and the user's input matches it.
|
|
13
17
|
* Proceed to Supabase for authoritative verification.
|
|
14
|
-
* 2. `no-cache`: No cached hash is available. Proceed to Supabase
|
|
15
|
-
* exponential backoff on repeated failures.
|
|
18
|
+
* 2. `no-cache`: No cached hash is available. Proceed to Supabase directly.
|
|
16
19
|
* - After a configurable number of consecutive local mismatches, the cached hash
|
|
17
20
|
* is invalidated (it may be stale from a server-side password change) and the
|
|
18
|
-
* guard falls back to
|
|
21
|
+
* guard falls back to direct Supabase mode.
|
|
22
|
+
*
|
|
23
|
+
* ## Lockout Tiers (persistent, survives page refresh)
|
|
24
|
+
*
|
|
25
|
+
* | Total failures | Lockout duration |
|
|
26
|
+
* |---------------|-----------------|
|
|
27
|
+
* | 5 | 5 minutes |
|
|
28
|
+
* | 10 | 30 minutes |
|
|
29
|
+
* | 15 | 2 hours |
|
|
30
|
+
* | 20+ | 24 hours |
|
|
31
|
+
*
|
|
32
|
+
* Counters reset to zero after any successful Supabase authentication.
|
|
19
33
|
*
|
|
20
34
|
* Security considerations:
|
|
21
|
-
* - The guard is a **client-side
|
|
35
|
+
* - The guard is a **client-side optimisation**, not a security boundary. It
|
|
22
36
|
* reduces unnecessary network calls and provides a better UX (instant
|
|
23
37
|
* rejection for wrong passwords) but Supabase remains the authoritative
|
|
24
38
|
* verifier.
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
39
|
+
* - Persistent lockout counters are stored in IndexedDB. An attacker with
|
|
40
|
+
* physical device access can clear IndexedDB, but Supabase's own server-side
|
|
41
|
+
* rate limiting is then the next line of defence.
|
|
27
42
|
* - Cached hashes are SHA-256 digests stored in IndexedDB. They are invalidated
|
|
28
43
|
* when stale-hash scenarios are detected (local match but Supabase rejects).
|
|
29
44
|
*
|
|
@@ -35,7 +50,7 @@
|
|
|
35
50
|
* - `'local-match'` -- The user's input matched a locally cached hash.
|
|
36
51
|
* Supabase is called for authoritative confirmation.
|
|
37
52
|
* - `'no-cache'` -- No local hash was available (or it was invalidated).
|
|
38
|
-
* Supabase is called directly
|
|
53
|
+
* Supabase is called directly.
|
|
39
54
|
*/
|
|
40
55
|
export type PreCheckStrategy = 'local-match' | 'no-cache';
|
|
41
56
|
/**
|
|
@@ -54,6 +69,26 @@ export type PreCheckResult = {
|
|
|
54
69
|
error: string;
|
|
55
70
|
retryAfterMs?: number;
|
|
56
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Check whether a persistent PIN lockout is currently active, without
|
|
74
|
+
* requiring the user to submit a PIN attempt.
|
|
75
|
+
*
|
|
76
|
+
* Call this on page/component mount to pre-populate the retry countdown so
|
|
77
|
+
* the UI immediately shows how long the user must wait rather than waiting
|
|
78
|
+
* for a failed submit to reveal the lockout.
|
|
79
|
+
*
|
|
80
|
+
* @returns Milliseconds remaining in the active lockout, or `0` if there is
|
|
81
|
+
* no active lockout.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* onMount(async () => {
|
|
86
|
+
* const remainingMs = await checkPersistentLockout();
|
|
87
|
+
* if (remainingMs > 0) startRetryCountdown(remainingMs);
|
|
88
|
+
* });
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function checkPersistentLockout(): Promise<number>;
|
|
57
92
|
/**
|
|
58
93
|
* Pre-check login credentials locally before calling Supabase.
|
|
59
94
|
*
|
|
@@ -84,8 +119,8 @@ export declare function preCheckLogin(input: string): Promise<PreCheckResult>;
|
|
|
84
119
|
/**
|
|
85
120
|
* Called after a successful Supabase login.
|
|
86
121
|
*
|
|
87
|
-
* Resets
|
|
88
|
-
*
|
|
122
|
+
* Resets the local failure counter and clears the persistent lockout record
|
|
123
|
+
* from IndexedDB so the user starts fresh.
|
|
89
124
|
*
|
|
90
125
|
* @example
|
|
91
126
|
* ```ts
|
|
@@ -93,7 +128,7 @@ export declare function preCheckLogin(input: string): Promise<PreCheckResult>;
|
|
|
93
128
|
* if (!error) onLoginSuccess();
|
|
94
129
|
* ```
|
|
95
130
|
*/
|
|
96
|
-
export declare function onLoginSuccess(): void
|
|
131
|
+
export declare function onLoginSuccess(): Promise<void>;
|
|
97
132
|
/**
|
|
98
133
|
* Called after a failed Supabase login.
|
|
99
134
|
*
|
|
@@ -101,9 +136,9 @@ export declare function onLoginSuccess(): void;
|
|
|
101
136
|
*
|
|
102
137
|
* - `'local-match'`: Supabase rejected a locally-matched password, meaning the
|
|
103
138
|
* cached hash is **stale** (password changed server-side). The cached hash is
|
|
104
|
-
* invalidated so future attempts go through
|
|
105
|
-
* - `'no-cache'`:
|
|
106
|
-
*
|
|
139
|
+
* invalidated so future attempts go through direct Supabase mode.
|
|
140
|
+
* - `'no-cache'`: Increments the persistent failure counter and applies a
|
|
141
|
+
* tier-based lockout if the threshold is reached.
|
|
107
142
|
*
|
|
108
143
|
* @param strategy - The {@link PreCheckStrategy} that was returned by
|
|
109
144
|
* {@link preCheckLogin} for this attempt.
|
|
@@ -121,14 +156,14 @@ export declare function onLoginFailure(strategy: PreCheckStrategy): Promise<void
|
|
|
121
156
|
/**
|
|
122
157
|
* Full reset of all login guard state.
|
|
123
158
|
*
|
|
124
|
-
* Call on sign-out or app reset to clear failure counters
|
|
125
|
-
*
|
|
159
|
+
* Call on sign-out or app reset to clear failure counters so the next login
|
|
160
|
+
* attempt starts with a clean slate.
|
|
126
161
|
*
|
|
127
162
|
* @example
|
|
128
163
|
* ```ts
|
|
129
164
|
* await supabase.auth.signOut();
|
|
130
|
-
* resetLoginGuard();
|
|
165
|
+
* await resetLoginGuard();
|
|
131
166
|
* ```
|
|
132
167
|
*/
|
|
133
|
-
export declare function resetLoginGuard(): void
|
|
168
|
+
export declare function resetLoginGuard(): Promise<void>;
|
|
134
169
|
//# sourceMappingURL=loginGuard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loginGuard.d.ts","sourceRoot":"","sources":["../../src/auth/loginGuard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loginGuard.d.ts","sourceRoot":"","sources":["../../src/auth/loginGuard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AA+CH;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,aAAa,GAAG,UAAU,CAAC;AAE1D;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,gBAAgB,CAAA;CAAE,GAC7C;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA8G7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC,CAQ9D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmE1E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAUpD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC9E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CASrD"}
|
package/dist/auth/loginGuard.js
CHANGED
|
@@ -1,36 +1,51 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @fileoverview Login Guard -- Local Credential Pre-Check &
|
|
2
|
+
* @fileoverview Login Guard -- Local Credential Pre-Check & Lockout
|
|
3
3
|
*
|
|
4
4
|
* Minimizes Supabase auth API requests by verifying credentials locally first.
|
|
5
5
|
* Only calls Supabase when the local hash matches (correct password) or when no
|
|
6
|
-
* local hash exists
|
|
6
|
+
* local hash exists.
|
|
7
7
|
*
|
|
8
8
|
* Architecture:
|
|
9
|
-
* - Maintains **in-memory
|
|
10
|
-
*
|
|
9
|
+
* - Maintains **in-memory** state for fast local-hash failure counters
|
|
10
|
+
* (resets on page refresh by design).
|
|
11
|
+
* - Maintains **persistent** lockout state in IndexedDB (`singleUserConfig`
|
|
12
|
+
* table, key `'pin_lockout'`). This survives page refreshes and tab
|
|
13
|
+
* closes, preventing brute-force attacks that rely on reloading to reset
|
|
14
|
+
* counters.
|
|
11
15
|
* - Two operational strategies:
|
|
12
16
|
* 1. `local-match`: A cached hash exists and the user's input matches it.
|
|
13
17
|
* Proceed to Supabase for authoritative verification.
|
|
14
|
-
* 2. `no-cache`: No cached hash is available. Proceed to Supabase
|
|
15
|
-
* exponential backoff on repeated failures.
|
|
18
|
+
* 2. `no-cache`: No cached hash is available. Proceed to Supabase directly.
|
|
16
19
|
* - After a configurable number of consecutive local mismatches, the cached hash
|
|
17
20
|
* is invalidated (it may be stale from a server-side password change) and the
|
|
18
|
-
* guard falls back to
|
|
21
|
+
* guard falls back to direct Supabase mode.
|
|
22
|
+
*
|
|
23
|
+
* ## Lockout Tiers (persistent, survives page refresh)
|
|
24
|
+
*
|
|
25
|
+
* | Total failures | Lockout duration |
|
|
26
|
+
* |---------------|-----------------|
|
|
27
|
+
* | 5 | 5 minutes |
|
|
28
|
+
* | 10 | 30 minutes |
|
|
29
|
+
* | 15 | 2 hours |
|
|
30
|
+
* | 20+ | 24 hours |
|
|
31
|
+
*
|
|
32
|
+
* Counters reset to zero after any successful Supabase authentication.
|
|
19
33
|
*
|
|
20
34
|
* Security considerations:
|
|
21
|
-
* - The guard is a **client-side
|
|
35
|
+
* - The guard is a **client-side optimisation**, not a security boundary. It
|
|
22
36
|
* reduces unnecessary network calls and provides a better UX (instant
|
|
23
37
|
* rejection for wrong passwords) but Supabase remains the authoritative
|
|
24
38
|
* verifier.
|
|
25
|
-
* -
|
|
26
|
-
*
|
|
39
|
+
* - Persistent lockout counters are stored in IndexedDB. An attacker with
|
|
40
|
+
* physical device access can clear IndexedDB, but Supabase's own server-side
|
|
41
|
+
* rate limiting is then the next line of defence.
|
|
27
42
|
* - Cached hashes are SHA-256 digests stored in IndexedDB. They are invalidated
|
|
28
43
|
* when stale-hash scenarios are detected (local match but Supabase rejects).
|
|
29
44
|
*
|
|
30
45
|
* @module auth/loginGuard
|
|
31
46
|
*/
|
|
32
47
|
import { hashValue } from './crypto';
|
|
33
|
-
import { getEngineConfig } from '../config';
|
|
48
|
+
import { getEngineConfig, waitForDb } from '../config';
|
|
34
49
|
import { debugLog, debugWarn } from '../debug';
|
|
35
50
|
// =============================================================================
|
|
36
51
|
// CONSTANTS
|
|
@@ -40,12 +55,21 @@ import { debugLog, debugWarn } from '../debug';
|
|
|
40
55
|
* invalidated. Prevents a permanently stale hash from locking out the user.
|
|
41
56
|
*/
|
|
42
57
|
const LOCAL_FAILURE_THRESHOLD = 5;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Progressive persistent-lockout tiers.
|
|
60
|
+
*
|
|
61
|
+
* After `failures` total failed Supabase login attempts (across page
|
|
62
|
+
* refreshes), the user is locked out for `durationMs` milliseconds.
|
|
63
|
+
* The tiers are checked in order — the **highest** matching tier wins.
|
|
64
|
+
*/
|
|
65
|
+
const PERSISTENT_LOCKOUT_TIERS = [
|
|
66
|
+
{ failures: 5, durationMs: 5 * 60000 }, // 5 min after 5 failures
|
|
67
|
+
{ failures: 10, durationMs: 30 * 60000 }, // 30 min after 10 failures
|
|
68
|
+
{ failures: 15, durationMs: 2 * 60 * 60000 }, // 2 hr after 15 failures
|
|
69
|
+
{ failures: 20, durationMs: 24 * 60 * 60000 } // 24 hr after 20+ failures
|
|
70
|
+
];
|
|
71
|
+
/** IndexedDB record key for the persistent lockout state. */
|
|
72
|
+
const PIN_LOCKOUT_KEY = 'pin_lockout';
|
|
49
73
|
// =============================================================================
|
|
50
74
|
// IN-MEMORY STATE
|
|
51
75
|
// =============================================================================
|
|
@@ -54,32 +78,9 @@ const BACKOFF_MULTIPLIER = 2;
|
|
|
54
78
|
* Once this reaches `LOCAL_FAILURE_THRESHOLD`, the cached hash is invalidated.
|
|
55
79
|
*/
|
|
56
80
|
let consecutiveLocalFailures = 0;
|
|
57
|
-
/**
|
|
58
|
-
* Number of failed Supabase login attempts in no-cache mode. Used to compute
|
|
59
|
-
* the exponential backoff delay.
|
|
60
|
-
*/
|
|
61
|
-
let rateLimitAttempts = 0;
|
|
62
|
-
/**
|
|
63
|
-
* Timestamp (ms since epoch) before which the next login attempt is blocked.
|
|
64
|
-
* Zero means no rate limit is active.
|
|
65
|
-
*/
|
|
66
|
-
let nextAllowedAttempt = 0;
|
|
67
81
|
// =============================================================================
|
|
68
82
|
// INTERNAL HELPERS
|
|
69
83
|
// =============================================================================
|
|
70
|
-
/**
|
|
71
|
-
* Check whether the current rate-limit window allows a new attempt.
|
|
72
|
-
*
|
|
73
|
-
* @returns An object indicating whether the attempt is allowed, and if not,
|
|
74
|
-
* how many milliseconds remain until the next allowed attempt.
|
|
75
|
-
*/
|
|
76
|
-
function checkRateLimit() {
|
|
77
|
-
const now = Date.now();
|
|
78
|
-
if (nextAllowedAttempt > now) {
|
|
79
|
-
return { allowed: false, retryAfterMs: nextAllowedAttempt - now };
|
|
80
|
-
}
|
|
81
|
-
return { allowed: true };
|
|
82
|
-
}
|
|
83
84
|
/**
|
|
84
85
|
* Invalidate the locally cached gate hash in IndexedDB.
|
|
85
86
|
*
|
|
@@ -108,9 +109,93 @@ async function invalidateCachedHash() {
|
|
|
108
109
|
debugWarn('[LoginGuard] Failed to invalidate cached hash:', e);
|
|
109
110
|
}
|
|
110
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Read the persistent lockout record from IndexedDB.
|
|
114
|
+
*
|
|
115
|
+
* Returns a zeroed default record when none exists or on any read error.
|
|
116
|
+
*/
|
|
117
|
+
async function readPersistentLockout() {
|
|
118
|
+
const zero = {
|
|
119
|
+
id: PIN_LOCKOUT_KEY,
|
|
120
|
+
failureCount: 0,
|
|
121
|
+
lockoutUntil: 0,
|
|
122
|
+
updatedAt: new Date().toISOString()
|
|
123
|
+
};
|
|
124
|
+
try {
|
|
125
|
+
await waitForDb();
|
|
126
|
+
const db = getEngineConfig().db;
|
|
127
|
+
if (!db)
|
|
128
|
+
return zero;
|
|
129
|
+
const record = await db.table('singleUserConfig').get(PIN_LOCKOUT_KEY);
|
|
130
|
+
return record ?? zero;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return zero;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Write a persistent lockout record to IndexedDB.
|
|
138
|
+
*
|
|
139
|
+
* Errors are swallowed — a failed write degrades gracefully to in-memory-only
|
|
140
|
+
* protection; Supabase server-side limits remain the primary defence.
|
|
141
|
+
*/
|
|
142
|
+
async function writePersistentLockout(record) {
|
|
143
|
+
try {
|
|
144
|
+
const db = getEngineConfig().db;
|
|
145
|
+
if (db) {
|
|
146
|
+
await db.table('singleUserConfig').put(record);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
debugWarn('[LoginGuard] Failed to write persistent lockout:', e);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Compute the lockout duration for a given failure count.
|
|
155
|
+
*
|
|
156
|
+
* Returns 0 when the count has not yet reached the first tier.
|
|
157
|
+
*/
|
|
158
|
+
function getLockoutDurationMs(failureCount) {
|
|
159
|
+
let duration = 0;
|
|
160
|
+
for (const tier of PERSISTENT_LOCKOUT_TIERS) {
|
|
161
|
+
if (failureCount >= tier.failures) {
|
|
162
|
+
duration = tier.durationMs;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return duration;
|
|
166
|
+
}
|
|
111
167
|
// =============================================================================
|
|
112
168
|
// PUBLIC API
|
|
113
169
|
// =============================================================================
|
|
170
|
+
/**
|
|
171
|
+
* Check whether a persistent PIN lockout is currently active, without
|
|
172
|
+
* requiring the user to submit a PIN attempt.
|
|
173
|
+
*
|
|
174
|
+
* Call this on page/component mount to pre-populate the retry countdown so
|
|
175
|
+
* the UI immediately shows how long the user must wait rather than waiting
|
|
176
|
+
* for a failed submit to reveal the lockout.
|
|
177
|
+
*
|
|
178
|
+
* @returns Milliseconds remaining in the active lockout, or `0` if there is
|
|
179
|
+
* no active lockout.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* onMount(async () => {
|
|
184
|
+
* const remainingMs = await checkPersistentLockout();
|
|
185
|
+
* if (remainingMs > 0) startRetryCountdown(remainingMs);
|
|
186
|
+
* });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export async function checkPersistentLockout() {
|
|
190
|
+
try {
|
|
191
|
+
const record = await readPersistentLockout();
|
|
192
|
+
const remaining = record.lockoutUntil - Date.now();
|
|
193
|
+
return remaining > 0 ? remaining : 0;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
114
199
|
/**
|
|
115
200
|
* Pre-check login credentials locally before calling Supabase.
|
|
116
201
|
*
|
|
@@ -139,6 +224,20 @@ async function invalidateCachedHash() {
|
|
|
139
224
|
*/
|
|
140
225
|
export async function preCheckLogin(input) {
|
|
141
226
|
try {
|
|
227
|
+
/* ── Persistent lockout check (survives page refresh) ── */
|
|
228
|
+
const lockoutRecord = await readPersistentLockout();
|
|
229
|
+
const persistentRemaining = lockoutRecord.lockoutUntil - Date.now();
|
|
230
|
+
if (persistentRemaining > 0) {
|
|
231
|
+
const mins = Math.ceil(persistentRemaining / 60000);
|
|
232
|
+
const timeDesc = persistentRemaining >= 60 * 60000
|
|
233
|
+
? `${Math.ceil(persistentRemaining / (60 * 60000))} hour${Math.ceil(persistentRemaining / (60 * 60000)) !== 1 ? 's' : ''}`
|
|
234
|
+
: `${mins} minute${mins !== 1 ? 's' : ''}`;
|
|
235
|
+
return {
|
|
236
|
+
proceed: false,
|
|
237
|
+
error: `Too many failed attempts. Please wait ${timeDesc} before trying again.`,
|
|
238
|
+
retryAfterMs: persistentRemaining
|
|
239
|
+
};
|
|
240
|
+
}
|
|
142
241
|
let cachedHash;
|
|
143
242
|
const config = getEngineConfig();
|
|
144
243
|
const db = config.db;
|
|
@@ -161,33 +260,16 @@ export async function preCheckLogin(input) {
|
|
|
161
260
|
if (consecutiveLocalFailures >= LOCAL_FAILURE_THRESHOLD) {
|
|
162
261
|
/* Threshold exceeded -- the cached hash may be stale (password changed
|
|
163
262
|
on another device). Invalidate it so subsequent attempts go directly
|
|
164
|
-
to Supabase
|
|
263
|
+
to Supabase. */
|
|
165
264
|
debugWarn('[LoginGuard] Threshold exceeded, invalidating cached hash');
|
|
166
265
|
await invalidateCachedHash();
|
|
167
266
|
consecutiveLocalFailures = 0;
|
|
168
|
-
/* Fall through to rate-limited Supabase mode */
|
|
169
|
-
const rateCheck = checkRateLimit();
|
|
170
|
-
if (!rateCheck.allowed) {
|
|
171
|
-
return {
|
|
172
|
-
proceed: false,
|
|
173
|
-
error: 'Too many attempts. Please wait before trying again.',
|
|
174
|
-
retryAfterMs: rateCheck.retryAfterMs
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
267
|
return { proceed: true, strategy: 'no-cache' };
|
|
178
268
|
}
|
|
179
269
|
return { proceed: false, error: 'Incorrect password or code' };
|
|
180
270
|
}
|
|
181
|
-
/* No cached hash --
|
|
182
|
-
|
|
183
|
-
if (!rateCheck.allowed) {
|
|
184
|
-
return {
|
|
185
|
-
proceed: false,
|
|
186
|
-
error: 'Too many attempts. Please wait before trying again.',
|
|
187
|
-
retryAfterMs: rateCheck.retryAfterMs
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
debugLog('[LoginGuard] No cached hash, proceeding to Supabase (rate-limited mode)');
|
|
271
|
+
/* No cached hash -- proceed to Supabase directly. */
|
|
272
|
+
debugLog('[LoginGuard] No cached hash, proceeding to Supabase');
|
|
191
273
|
return { proceed: true, strategy: 'no-cache' };
|
|
192
274
|
}
|
|
193
275
|
catch (e) {
|
|
@@ -201,8 +283,8 @@ export async function preCheckLogin(input) {
|
|
|
201
283
|
/**
|
|
202
284
|
* Called after a successful Supabase login.
|
|
203
285
|
*
|
|
204
|
-
* Resets
|
|
205
|
-
*
|
|
286
|
+
* Resets the local failure counter and clears the persistent lockout record
|
|
287
|
+
* from IndexedDB so the user starts fresh.
|
|
206
288
|
*
|
|
207
289
|
* @example
|
|
208
290
|
* ```ts
|
|
@@ -210,10 +292,15 @@ export async function preCheckLogin(input) {
|
|
|
210
292
|
* if (!error) onLoginSuccess();
|
|
211
293
|
* ```
|
|
212
294
|
*/
|
|
213
|
-
export function onLoginSuccess() {
|
|
295
|
+
export async function onLoginSuccess() {
|
|
214
296
|
consecutiveLocalFailures = 0;
|
|
215
|
-
|
|
216
|
-
|
|
297
|
+
/* Clear persistent failure counter and lockout */
|
|
298
|
+
await writePersistentLockout({
|
|
299
|
+
id: PIN_LOCKOUT_KEY,
|
|
300
|
+
failureCount: 0,
|
|
301
|
+
lockoutUntil: 0,
|
|
302
|
+
updatedAt: new Date().toISOString()
|
|
303
|
+
});
|
|
217
304
|
debugLog('[LoginGuard] Login success, counters reset');
|
|
218
305
|
}
|
|
219
306
|
/**
|
|
@@ -223,9 +310,9 @@ export function onLoginSuccess() {
|
|
|
223
310
|
*
|
|
224
311
|
* - `'local-match'`: Supabase rejected a locally-matched password, meaning the
|
|
225
312
|
* cached hash is **stale** (password changed server-side). The cached hash is
|
|
226
|
-
* invalidated so future attempts go through
|
|
227
|
-
* - `'no-cache'`:
|
|
228
|
-
*
|
|
313
|
+
* invalidated so future attempts go through direct Supabase mode.
|
|
314
|
+
* - `'no-cache'`: Increments the persistent failure counter and applies a
|
|
315
|
+
* tier-based lockout if the threshold is reached.
|
|
229
316
|
*
|
|
230
317
|
* @param strategy - The {@link PreCheckStrategy} that was returned by
|
|
231
318
|
* {@link preCheckLogin} for this attempt.
|
|
@@ -247,30 +334,49 @@ export async function onLoginFailure(strategy) {
|
|
|
247
334
|
await invalidateCachedHash();
|
|
248
335
|
}
|
|
249
336
|
else {
|
|
250
|
-
/* No-cache mode:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
337
|
+
/* No-cache mode: increment persistent failure counter and apply
|
|
338
|
+
tier-based lockout if the threshold is reached. */
|
|
339
|
+
try {
|
|
340
|
+
const record = await readPersistentLockout();
|
|
341
|
+
const newCount = record.failureCount + 1;
|
|
342
|
+
const lockoutDuration = getLockoutDurationMs(newCount);
|
|
343
|
+
const lockoutUntil = lockoutDuration > 0 ? Date.now() + lockoutDuration : 0;
|
|
344
|
+
if (lockoutUntil > 0) {
|
|
345
|
+
const mins = Math.ceil(lockoutDuration / 60000);
|
|
346
|
+
debugWarn(`[LoginGuard] Persistent lockout applied: ${mins} min (${newCount} total failures)`);
|
|
347
|
+
}
|
|
348
|
+
await writePersistentLockout({
|
|
349
|
+
id: PIN_LOCKOUT_KEY,
|
|
350
|
+
failureCount: newCount,
|
|
351
|
+
lockoutUntil,
|
|
352
|
+
updatedAt: new Date().toISOString()
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
debugWarn('[LoginGuard] Failed to update persistent lockout:', e);
|
|
357
|
+
}
|
|
256
358
|
}
|
|
257
359
|
}
|
|
258
360
|
/**
|
|
259
361
|
* Full reset of all login guard state.
|
|
260
362
|
*
|
|
261
|
-
* Call on sign-out or app reset to clear failure counters
|
|
262
|
-
*
|
|
363
|
+
* Call on sign-out or app reset to clear failure counters so the next login
|
|
364
|
+
* attempt starts with a clean slate.
|
|
263
365
|
*
|
|
264
366
|
* @example
|
|
265
367
|
* ```ts
|
|
266
368
|
* await supabase.auth.signOut();
|
|
267
|
-
* resetLoginGuard();
|
|
369
|
+
* await resetLoginGuard();
|
|
268
370
|
* ```
|
|
269
371
|
*/
|
|
270
|
-
export function resetLoginGuard() {
|
|
372
|
+
export async function resetLoginGuard() {
|
|
271
373
|
consecutiveLocalFailures = 0;
|
|
272
|
-
|
|
273
|
-
|
|
374
|
+
await writePersistentLockout({
|
|
375
|
+
id: PIN_LOCKOUT_KEY,
|
|
376
|
+
failureCount: 0,
|
|
377
|
+
lockoutUntil: 0,
|
|
378
|
+
updatedAt: new Date().toISOString()
|
|
379
|
+
});
|
|
274
380
|
debugLog('[LoginGuard] Guard reset');
|
|
275
381
|
}
|
|
276
382
|
//# sourceMappingURL=loginGuard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loginGuard.js","sourceRoot":"","sources":["../../src/auth/loginGuard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loginGuard.js","sourceRoot":"","sources":["../../src/auth/loginGuard.ts"],"names":[],"mappings":"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"}
|