@prabhask5/stellar-engine 1.1.8 → 1.1.10
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 +19 -26
- package/dist/auth/crypto.d.ts +0 -23
- package/dist/auth/crypto.d.ts.map +1 -1
- package/dist/auth/crypto.js +0 -25
- package/dist/auth/crypto.js.map +1 -1
- package/dist/auth/deviceVerification.d.ts +2 -2
- package/dist/auth/deviceVerification.js +2 -2
- package/dist/auth/loginGuard.d.ts +7 -14
- package/dist/auth/loginGuard.d.ts.map +1 -1
- package/dist/auth/loginGuard.js +27 -62
- package/dist/auth/loginGuard.js.map +1 -1
- package/dist/auth/offlineCredentials.d.ts +6 -59
- package/dist/auth/offlineCredentials.d.ts.map +1 -1
- package/dist/auth/offlineCredentials.js +8 -111
- package/dist/auth/offlineCredentials.js.map +1 -1
- package/dist/auth/resolveAuthState.d.ts +14 -18
- package/dist/auth/resolveAuthState.d.ts.map +1 -1
- package/dist/auth/resolveAuthState.js +16 -58
- package/dist/auth/resolveAuthState.js.map +1 -1
- package/dist/auth/singleUser.js +4 -4
- package/dist/auth/singleUser.js.map +1 -1
- package/dist/bin/install-pwa.d.ts +4 -2
- package/dist/bin/install-pwa.d.ts.map +1 -1
- package/dist/bin/install-pwa.js +2289 -249
- package/dist/bin/install-pwa.js.map +1 -1
- package/dist/config.d.ts +3 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/data.d.ts +105 -0
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +126 -0
- package/dist/data.js.map +1 -1
- package/dist/entries/auth.d.ts +8 -13
- package/dist/entries/auth.d.ts.map +1 -1
- package/dist/entries/auth.js +11 -40
- package/dist/entries/auth.js.map +1 -1
- package/dist/entries/stores.d.ts +2 -0
- package/dist/entries/stores.d.ts.map +1 -1
- package/dist/entries/stores.js +7 -0
- package/dist/entries/stores.js.map +1 -1
- package/dist/entries/types.d.ts +0 -1
- package/dist/entries/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -18
- package/dist/index.js.map +1 -1
- package/dist/kit/loads.d.ts +3 -5
- package/dist/kit/loads.d.ts.map +1 -1
- package/dist/kit/loads.js +2 -10
- package/dist/kit/loads.js.map +1 -1
- package/dist/stores/authState.d.ts +2 -2
- package/dist/stores/authState.js +1 -1
- package/dist/stores/factories.d.ts +138 -0
- package/dist/stores/factories.d.ts.map +1 -0
- package/dist/stores/factories.js +154 -0
- package/dist/stores/factories.js.map +1 -0
- package/dist/supabase/auth.d.ts +14 -181
- package/dist/supabase/auth.d.ts.map +1 -1
- package/dist/supabase/auth.js +17 -317
- package/dist/supabase/auth.js.map +1 -1
- package/package.json +1 -1
- package/dist/auth/admin.d.ts +0 -49
- package/dist/auth/admin.d.ts.map +0 -1
- package/dist/auth/admin.js +0 -66
- package/dist/auth/admin.js.map +0 -1
- package/dist/auth/offlineLogin.d.ts +0 -120
- package/dist/auth/offlineLogin.d.ts.map +0 -1
- package/dist/auth/offlineLogin.js +0 -142
- package/dist/auth/offlineLogin.js.map +0 -1
package/dist/supabase/auth.d.ts
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Supabase Authentication Module
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
*
|
|
4
|
+
* Provides core authentication utilities on top of Supabase Auth for the
|
|
5
|
+
* single-user PIN/password gate system:
|
|
6
6
|
*
|
|
7
|
-
* - **
|
|
8
|
-
*
|
|
9
|
-
* is offline (airplane mode, poor connectivity, etc.).
|
|
7
|
+
* - **Sign-out & teardown**: Full 10-step teardown sequence to ensure no stale
|
|
8
|
+
* data leaks across sessions.
|
|
10
9
|
*
|
|
11
|
-
* - **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* - **Session management**: `getSession()` falls back to localStorage when the
|
|
11
|
+
* device is offline, ensuring the app can still render authenticated views
|
|
12
|
+
* with stale-but-usable session data.
|
|
14
13
|
*
|
|
15
|
-
* - **
|
|
16
|
-
*
|
|
17
|
-
* session until the device is verified.
|
|
14
|
+
* - **Profile CRUD**: Read and update user profile metadata on Supabase and
|
|
15
|
+
* in the offline credential cache.
|
|
18
16
|
*
|
|
19
|
-
* - **
|
|
20
|
-
*
|
|
21
|
-
* views with stale-but-usable session data.
|
|
17
|
+
* - **Email confirmation & OTP**: Resend confirmation emails and verify OTP
|
|
18
|
+
* token hashes from confirmation links.
|
|
22
19
|
*
|
|
23
20
|
* Security considerations:
|
|
24
|
-
* - Passwords are hashed before being stored in the offline credential cache.
|
|
25
|
-
* - The `changePassword` flow verifies the current password locally (if a
|
|
26
|
-
* cached hash exists) or via a Supabase re-authentication call.
|
|
27
21
|
* - Corrupted sessions are detected and automatically cleared to prevent
|
|
28
22
|
* infinite error loops.
|
|
29
23
|
* - Sign-out follows a strict 10-step teardown sequence to ensure no stale
|
|
30
24
|
* data leaks across user boundaries.
|
|
31
25
|
*
|
|
32
26
|
* Integration patterns:
|
|
33
|
-
* -
|
|
27
|
+
* - Used internally by single-user auth (`../auth/singleUser.ts`) and the
|
|
28
|
+
* scaffolded confirm page (`../kit/confirm.ts`).
|
|
34
29
|
* - Works in tandem with `./client.ts` (lazy Supabase singleton) and
|
|
35
30
|
* `../engine.ts` (sync engine lifecycle).
|
|
36
31
|
* - Offline credential helpers live in `../auth/offlineCredentials.ts`.
|
|
@@ -38,89 +33,6 @@
|
|
|
38
33
|
* @module supabase/auth
|
|
39
34
|
*/
|
|
40
35
|
import type { User, Session } from '@supabase/supabase-js';
|
|
41
|
-
/**
|
|
42
|
-
* Standardized response shape returned by all authentication operations.
|
|
43
|
-
*
|
|
44
|
-
* Every auth function in this module returns this interface so that callers
|
|
45
|
-
* can rely on a single, predictable contract for success/failure handling.
|
|
46
|
-
*/
|
|
47
|
-
export interface AuthResponse {
|
|
48
|
-
/** The authenticated Supabase user, or `null` if authentication failed. */
|
|
49
|
-
user: User | null;
|
|
50
|
-
/** The active session, or `null` if not yet established (e.g. device verification pending). */
|
|
51
|
-
session: Session | null;
|
|
52
|
-
/** A human-readable error message, or `null` on success. */
|
|
53
|
-
error: string | null;
|
|
54
|
-
/**
|
|
55
|
-
* When `true`, the device has not been verified and the caller must
|
|
56
|
-
* present a device-verification OTP input before granting access.
|
|
57
|
-
* Only set when `auth.deviceVerification.enabled` is `true` in the engine config.
|
|
58
|
-
*/
|
|
59
|
-
deviceVerificationRequired?: boolean;
|
|
60
|
-
/**
|
|
61
|
-
* A partially-masked version of the user's email (e.g. `j***@example.com`)
|
|
62
|
-
* shown during device verification so the user knows where to look for the OTP.
|
|
63
|
-
*/
|
|
64
|
-
maskedEmail?: string;
|
|
65
|
-
/**
|
|
66
|
-
* If the login guard rejected the attempt due to rate-limiting, this value
|
|
67
|
-
* indicates how many milliseconds the caller should wait before retrying.
|
|
68
|
-
*/
|
|
69
|
-
retryAfterMs?: number;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Authenticate a user with email and password.
|
|
73
|
-
*
|
|
74
|
-
* Flow:
|
|
75
|
-
* 1. Run `preCheckLogin` to enforce local brute-force / rate-limit rules.
|
|
76
|
-
* 2. Call `supabase.auth.signInWithPassword`.
|
|
77
|
-
* 3. On success, cache credentials for offline re-authentication.
|
|
78
|
-
* 4. If device verification is enabled, check trust status and optionally
|
|
79
|
-
* trigger an OTP challenge instead of returning the session.
|
|
80
|
-
*
|
|
81
|
-
* @param email - The user's email address.
|
|
82
|
-
* @param password - The user's plaintext password (hashed before caching).
|
|
83
|
-
* @returns An {@link AuthResponse} indicating success, failure, or a device
|
|
84
|
-
* verification challenge.
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```ts
|
|
88
|
-
* const result = await signIn('user@example.com', 's3cret');
|
|
89
|
-
* if (result.deviceVerificationRequired) {
|
|
90
|
-
* // Show OTP input, display result.maskedEmail
|
|
91
|
-
* } else if (result.error) {
|
|
92
|
-
* // Show error
|
|
93
|
-
* } else {
|
|
94
|
-
* // Logged in — result.session is available
|
|
95
|
-
* }
|
|
96
|
-
* ```
|
|
97
|
-
*
|
|
98
|
-
* @see {@link preCheckLogin} — local credential & rate-limit guard
|
|
99
|
-
* @see {@link cacheOfflineCredentials} — offline credential persistence
|
|
100
|
-
*/
|
|
101
|
-
export declare function signIn(email: string, password: string): Promise<AuthResponse>;
|
|
102
|
-
/**
|
|
103
|
-
* Register a new user account with Supabase.
|
|
104
|
-
*
|
|
105
|
-
* Profile data is transformed via the optional `auth.profileToMetadata`
|
|
106
|
-
* config hook before being sent as `user_metadata` so that the host app
|
|
107
|
-
* can normalize field names.
|
|
108
|
-
*
|
|
109
|
-
* @param email - The new user's email address.
|
|
110
|
-
* @param password - The desired password (Supabase enforces its own strength rules).
|
|
111
|
-
* @param profileData - Arbitrary profile fields (e.g. `{ display_name, avatar_url }`).
|
|
112
|
-
* @returns An {@link AuthResponse}. Note: `session` may be `null` if email
|
|
113
|
-
* confirmation is required by the Supabase project settings.
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* ```ts
|
|
117
|
-
* const result = await signUp('new@user.com', 'p@ssw0rd', { display_name: 'Ada' });
|
|
118
|
-
* if (result.error) { ... }
|
|
119
|
-
* ```
|
|
120
|
-
*
|
|
121
|
-
* @see {@link getConfirmRedirectUrl} — determines the email confirmation link target
|
|
122
|
-
*/
|
|
123
|
-
export declare function signUp(email: string, password: string, profileData: Record<string, unknown>): Promise<AuthResponse>;
|
|
124
36
|
/**
|
|
125
37
|
* Sign the current user out and perform a full teardown of local state.
|
|
126
38
|
*
|
|
@@ -145,7 +57,7 @@ export declare function signUp(email: string, password: string, profileData: Rec
|
|
|
145
57
|
* @param options.preserveOfflineCredentials - When `true`, offline credentials
|
|
146
58
|
* are kept so the user can re-authenticate without network access.
|
|
147
59
|
* @param options.preserveLocalData - When `true`, pending sync queue and local
|
|
148
|
-
* cache are retained
|
|
60
|
+
* cache are retained.
|
|
149
61
|
* @returns An object with an `error` field (`null` on success).
|
|
150
62
|
*
|
|
151
63
|
* @example
|
|
@@ -245,85 +157,6 @@ export declare function getUserProfile(user: User | null): Record<string, unknow
|
|
|
245
157
|
export declare function updateProfile(profile: Record<string, unknown>): Promise<{
|
|
246
158
|
error: string | null;
|
|
247
159
|
}>;
|
|
248
|
-
/**
|
|
249
|
-
* Change the current user's password.
|
|
250
|
-
*
|
|
251
|
-
* Security flow:
|
|
252
|
-
* 1. Retrieve the current session to obtain the user's email.
|
|
253
|
-
* 2. **Verify the current password** — two strategies:
|
|
254
|
-
* a. If an offline credential cache exists and the email matches, compare
|
|
255
|
-
* hashes locally (avoids a network round-trip and an extra Supabase call).
|
|
256
|
-
* b. Otherwise, fall back to `supabase.auth.signInWithPassword` to verify
|
|
257
|
-
* against the server.
|
|
258
|
-
* 3. Call `supabase.auth.updateUser({ password })` to set the new password.
|
|
259
|
-
* 4. Update the offline credential cache with the new password hash.
|
|
260
|
-
*
|
|
261
|
-
* @param currentPassword - The user's current password (for verification).
|
|
262
|
-
* @param newPassword - The desired new password.
|
|
263
|
-
* @returns An object with an `error` field (`null` on success).
|
|
264
|
-
*
|
|
265
|
-
* @throws Never throws — all errors are returned in the `error` field.
|
|
266
|
-
*
|
|
267
|
-
* @example
|
|
268
|
-
* ```ts
|
|
269
|
-
* const { error } = await changePassword('oldPass', 'newPass');
|
|
270
|
-
* if (error) { alert(error); }
|
|
271
|
-
* ```
|
|
272
|
-
*
|
|
273
|
-
* @see {@link hashValue} — used for local password comparison
|
|
274
|
-
* @see {@link updateOfflineCredentialsPassword} — keeps the offline cache in sync
|
|
275
|
-
*/
|
|
276
|
-
export declare function changePassword(currentPassword: string, newPassword: string): Promise<{
|
|
277
|
-
error: string | null;
|
|
278
|
-
}>;
|
|
279
|
-
/**
|
|
280
|
-
* Initiate an email change for the current user.
|
|
281
|
-
*
|
|
282
|
-
* Supabase sends a confirmation link to the **new** email address. The
|
|
283
|
-
* change is not applied until the user clicks that link and the app calls
|
|
284
|
-
* {@link completeEmailChange}.
|
|
285
|
-
*
|
|
286
|
-
* @param newEmail - The desired new email address.
|
|
287
|
-
* @returns An object indicating whether a confirmation email was sent, plus
|
|
288
|
-
* any error that occurred.
|
|
289
|
-
*
|
|
290
|
-
* @example
|
|
291
|
-
* ```ts
|
|
292
|
-
* const { error, confirmationRequired } = await changeEmail('new@example.com');
|
|
293
|
-
* if (confirmationRequired) {
|
|
294
|
-
* // Tell the user to check their inbox
|
|
295
|
-
* }
|
|
296
|
-
* ```
|
|
297
|
-
*
|
|
298
|
-
* @see {@link completeEmailChange} — finishes the flow after confirmation
|
|
299
|
-
*/
|
|
300
|
-
export declare function changeEmail(newEmail: string): Promise<{
|
|
301
|
-
error: string | null;
|
|
302
|
-
confirmationRequired: boolean;
|
|
303
|
-
}>;
|
|
304
|
-
/**
|
|
305
|
-
* Complete an email change after the user confirms via the email link.
|
|
306
|
-
*
|
|
307
|
-
* Refreshes the Supabase session to pick up the updated email address,
|
|
308
|
-
* then updates the offline credential cache so that offline login uses
|
|
309
|
-
* the new email.
|
|
310
|
-
*
|
|
311
|
-
* @returns An object containing the new email and/or an error message.
|
|
312
|
-
*
|
|
313
|
-
* @example
|
|
314
|
-
* ```ts
|
|
315
|
-
* const { error, newEmail } = await completeEmailChange();
|
|
316
|
-
* if (!error) {
|
|
317
|
-
* console.log(`Email changed to ${newEmail}`);
|
|
318
|
-
* }
|
|
319
|
-
* ```
|
|
320
|
-
*
|
|
321
|
-
* @see {@link changeEmail} — initiates the flow
|
|
322
|
-
*/
|
|
323
|
-
export declare function completeEmailChange(): Promise<{
|
|
324
|
-
error: string | null;
|
|
325
|
-
newEmail: string | null;
|
|
326
|
-
}>;
|
|
327
160
|
/**
|
|
328
161
|
* Resend the signup confirmation email for a given address.
|
|
329
162
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/supabase/auth.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/supabase/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AA0C3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,OAAO,CAAC,OAAO,CAAC,EAAE;IACtC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAgEpC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA4C1D;AA2CD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,OAAO,CAMjE;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAoBnC;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAU9F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,SAAS,CAC7B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,cAAc,GACxC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAOnC;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAK/D"}
|
package/dist/supabase/auth.js
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Supabase Authentication Module
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
*
|
|
4
|
+
* Provides core authentication utilities on top of Supabase Auth for the
|
|
5
|
+
* single-user PIN/password gate system:
|
|
6
6
|
*
|
|
7
|
-
* - **
|
|
8
|
-
*
|
|
9
|
-
* is offline (airplane mode, poor connectivity, etc.).
|
|
7
|
+
* - **Sign-out & teardown**: Full 10-step teardown sequence to ensure no stale
|
|
8
|
+
* data leaks across sessions.
|
|
10
9
|
*
|
|
11
|
-
* - **
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* - **Session management**: `getSession()` falls back to localStorage when the
|
|
11
|
+
* device is offline, ensuring the app can still render authenticated views
|
|
12
|
+
* with stale-but-usable session data.
|
|
14
13
|
*
|
|
15
|
-
* - **
|
|
16
|
-
*
|
|
17
|
-
* session until the device is verified.
|
|
14
|
+
* - **Profile CRUD**: Read and update user profile metadata on Supabase and
|
|
15
|
+
* in the offline credential cache.
|
|
18
16
|
*
|
|
19
|
-
* - **
|
|
20
|
-
*
|
|
21
|
-
* views with stale-but-usable session data.
|
|
17
|
+
* - **Email confirmation & OTP**: Resend confirmation emails and verify OTP
|
|
18
|
+
* token hashes from confirmation links.
|
|
22
19
|
*
|
|
23
20
|
* Security considerations:
|
|
24
|
-
* - Passwords are hashed before being stored in the offline credential cache.
|
|
25
|
-
* - The `changePassword` flow verifies the current password locally (if a
|
|
26
|
-
* cached hash exists) or via a Supabase re-authentication call.
|
|
27
21
|
* - Corrupted sessions are detected and automatically cleared to prevent
|
|
28
22
|
* infinite error loops.
|
|
29
23
|
* - Sign-out follows a strict 10-step teardown sequence to ensure no stale
|
|
30
24
|
* data leaks across user boundaries.
|
|
31
25
|
*
|
|
32
26
|
* Integration patterns:
|
|
33
|
-
* -
|
|
27
|
+
* - Used internally by single-user auth (`../auth/singleUser.ts`) and the
|
|
28
|
+
* scaffolded confirm page (`../kit/confirm.ts`).
|
|
34
29
|
* - Works in tandem with `./client.ts` (lazy Supabase singleton) and
|
|
35
30
|
* `../engine.ts` (sync engine lifecycle).
|
|
36
31
|
* - Offline credential helpers live in `../auth/offlineCredentials.ts`.
|
|
@@ -38,10 +33,9 @@
|
|
|
38
33
|
* @module supabase/auth
|
|
39
34
|
*/
|
|
40
35
|
import { supabase } from './client';
|
|
41
|
-
import {
|
|
36
|
+
import { clearOfflineCredentials, updateOfflineCredentialsProfile } from '../auth/offlineCredentials';
|
|
42
37
|
import { clearOfflineSession } from '../auth/offlineSession';
|
|
43
|
-
import {
|
|
44
|
-
import { hashValue, isAlreadyHashed } from '../auth/crypto';
|
|
38
|
+
import { resetLoginGuard } from '../auth/loginGuard';
|
|
45
39
|
import { debugWarn, debugError } from '../debug';
|
|
46
40
|
import { getEngineConfig } from '../config';
|
|
47
41
|
import { syncStatusStore } from '../stores/sync';
|
|
@@ -60,8 +54,7 @@ import { authState } from '../stores/authState';
|
|
|
60
54
|
* Falls back to a relative `/confirm` path in SSR environments where
|
|
61
55
|
* `window` is unavailable.
|
|
62
56
|
*
|
|
63
|
-
* @see {@link
|
|
64
|
-
* @see {@link resendConfirmationEmail} — also uses this URL
|
|
57
|
+
* @see {@link resendConfirmationEmail} — uses this URL
|
|
65
58
|
*/
|
|
66
59
|
function getConfirmRedirectUrl() {
|
|
67
60
|
if (typeof window !== 'undefined') {
|
|
@@ -72,140 +65,6 @@ function getConfirmRedirectUrl() {
|
|
|
72
65
|
return '/confirm';
|
|
73
66
|
}
|
|
74
67
|
// =============================================================================
|
|
75
|
-
// SECTION: Sign In
|
|
76
|
-
// =============================================================================
|
|
77
|
-
/**
|
|
78
|
-
* Authenticate a user with email and password.
|
|
79
|
-
*
|
|
80
|
-
* Flow:
|
|
81
|
-
* 1. Run `preCheckLogin` to enforce local brute-force / rate-limit rules.
|
|
82
|
-
* 2. Call `supabase.auth.signInWithPassword`.
|
|
83
|
-
* 3. On success, cache credentials for offline re-authentication.
|
|
84
|
-
* 4. If device verification is enabled, check trust status and optionally
|
|
85
|
-
* trigger an OTP challenge instead of returning the session.
|
|
86
|
-
*
|
|
87
|
-
* @param email - The user's email address.
|
|
88
|
-
* @param password - The user's plaintext password (hashed before caching).
|
|
89
|
-
* @returns An {@link AuthResponse} indicating success, failure, or a device
|
|
90
|
-
* verification challenge.
|
|
91
|
-
*
|
|
92
|
-
* @example
|
|
93
|
-
* ```ts
|
|
94
|
-
* const result = await signIn('user@example.com', 's3cret');
|
|
95
|
-
* if (result.deviceVerificationRequired) {
|
|
96
|
-
* // Show OTP input, display result.maskedEmail
|
|
97
|
-
* } else if (result.error) {
|
|
98
|
-
* // Show error
|
|
99
|
-
* } else {
|
|
100
|
-
* // Logged in — result.session is available
|
|
101
|
-
* }
|
|
102
|
-
* ```
|
|
103
|
-
*
|
|
104
|
-
* @see {@link preCheckLogin} — local credential & rate-limit guard
|
|
105
|
-
* @see {@link cacheOfflineCredentials} — offline credential persistence
|
|
106
|
-
*/
|
|
107
|
-
export async function signIn(email, password) {
|
|
108
|
-
// Pre-check credentials locally before calling Supabase
|
|
109
|
-
const preCheck = await preCheckLogin(password, 'multi-user', email);
|
|
110
|
-
if (!preCheck.proceed) {
|
|
111
|
-
return {
|
|
112
|
-
user: null,
|
|
113
|
-
session: null,
|
|
114
|
-
error: preCheck.error,
|
|
115
|
-
retryAfterMs: preCheck.retryAfterMs
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
const strategy = preCheck.strategy;
|
|
119
|
-
const { data, error } = await supabase.auth.signInWithPassword({
|
|
120
|
-
email,
|
|
121
|
-
password
|
|
122
|
-
});
|
|
123
|
-
if (error) {
|
|
124
|
-
await onLoginFailure(strategy, 'multi-user');
|
|
125
|
-
return { user: data.user, session: data.session, error: error.message };
|
|
126
|
-
}
|
|
127
|
-
// Successful Supabase login
|
|
128
|
-
onLoginSuccess();
|
|
129
|
-
// Cache credentials for offline use on successful login
|
|
130
|
-
if (data.session && data.user) {
|
|
131
|
-
try {
|
|
132
|
-
await cacheOfflineCredentials(email, password, data.user, data.session);
|
|
133
|
-
}
|
|
134
|
-
catch (e) {
|
|
135
|
-
debugError('[Auth] Failed to cache offline credentials:', e);
|
|
136
|
-
}
|
|
137
|
-
// Check device verification for multi-user mode
|
|
138
|
-
const config = getEngineConfig();
|
|
139
|
-
if (config.auth?.deviceVerification?.enabled) {
|
|
140
|
-
const { isDeviceTrusted, touchTrustedDevice, sendDeviceVerification, maskEmail } = await import('../auth/deviceVerification');
|
|
141
|
-
const trusted = await isDeviceTrusted(data.user.id);
|
|
142
|
-
if (!trusted) {
|
|
143
|
-
/* Untrusted device — we intentionally do NOT return the session here.
|
|
144
|
-
The caller must complete the device-verification OTP flow first.
|
|
145
|
-
The session will be re-established after verification succeeds. */
|
|
146
|
-
const maskedEmail = maskEmail(email);
|
|
147
|
-
await sendDeviceVerification(email);
|
|
148
|
-
return {
|
|
149
|
-
user: data.user,
|
|
150
|
-
session: null,
|
|
151
|
-
error: null,
|
|
152
|
-
deviceVerificationRequired: true,
|
|
153
|
-
maskedEmail
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
await touchTrustedDevice(data.user.id);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
return {
|
|
160
|
-
user: data.user,
|
|
161
|
-
session: data.session,
|
|
162
|
-
error: null
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
// =============================================================================
|
|
166
|
-
// SECTION: Sign Up
|
|
167
|
-
// =============================================================================
|
|
168
|
-
/**
|
|
169
|
-
* Register a new user account with Supabase.
|
|
170
|
-
*
|
|
171
|
-
* Profile data is transformed via the optional `auth.profileToMetadata`
|
|
172
|
-
* config hook before being sent as `user_metadata` so that the host app
|
|
173
|
-
* can normalize field names.
|
|
174
|
-
*
|
|
175
|
-
* @param email - The new user's email address.
|
|
176
|
-
* @param password - The desired password (Supabase enforces its own strength rules).
|
|
177
|
-
* @param profileData - Arbitrary profile fields (e.g. `{ display_name, avatar_url }`).
|
|
178
|
-
* @returns An {@link AuthResponse}. Note: `session` may be `null` if email
|
|
179
|
-
* confirmation is required by the Supabase project settings.
|
|
180
|
-
*
|
|
181
|
-
* @example
|
|
182
|
-
* ```ts
|
|
183
|
-
* const result = await signUp('new@user.com', 'p@ssw0rd', { display_name: 'Ada' });
|
|
184
|
-
* if (result.error) { ... }
|
|
185
|
-
* ```
|
|
186
|
-
*
|
|
187
|
-
* @see {@link getConfirmRedirectUrl} — determines the email confirmation link target
|
|
188
|
-
*/
|
|
189
|
-
export async function signUp(email, password, profileData) {
|
|
190
|
-
const config = getEngineConfig();
|
|
191
|
-
const metadata = config.auth?.profileToMetadata
|
|
192
|
-
? config.auth.profileToMetadata(profileData)
|
|
193
|
-
: profileData;
|
|
194
|
-
const { data, error } = await supabase.auth.signUp({
|
|
195
|
-
email,
|
|
196
|
-
password,
|
|
197
|
-
options: {
|
|
198
|
-
emailRedirectTo: getConfirmRedirectUrl(),
|
|
199
|
-
data: metadata
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
return {
|
|
203
|
-
user: data.user,
|
|
204
|
-
session: data.session,
|
|
205
|
-
error: error?.message || null
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
// =============================================================================
|
|
209
68
|
// SECTION: Sign Out
|
|
210
69
|
// =============================================================================
|
|
211
70
|
/**
|
|
@@ -232,7 +91,7 @@ export async function signUp(email, password, profileData) {
|
|
|
232
91
|
* @param options.preserveOfflineCredentials - When `true`, offline credentials
|
|
233
92
|
* are kept so the user can re-authenticate without network access.
|
|
234
93
|
* @param options.preserveLocalData - When `true`, pending sync queue and local
|
|
235
|
-
* cache are retained
|
|
94
|
+
* cache are retained.
|
|
236
95
|
* @returns An object with an `error` field (`null` on success).
|
|
237
96
|
*
|
|
238
97
|
* @example
|
|
@@ -501,165 +360,6 @@ export async function updateProfile(profile) {
|
|
|
501
360
|
return { error: error?.message || null };
|
|
502
361
|
}
|
|
503
362
|
// =============================================================================
|
|
504
|
-
// SECTION: Password Management
|
|
505
|
-
// =============================================================================
|
|
506
|
-
/**
|
|
507
|
-
* Change the current user's password.
|
|
508
|
-
*
|
|
509
|
-
* Security flow:
|
|
510
|
-
* 1. Retrieve the current session to obtain the user's email.
|
|
511
|
-
* 2. **Verify the current password** — two strategies:
|
|
512
|
-
* a. If an offline credential cache exists and the email matches, compare
|
|
513
|
-
* hashes locally (avoids a network round-trip and an extra Supabase call).
|
|
514
|
-
* b. Otherwise, fall back to `supabase.auth.signInWithPassword` to verify
|
|
515
|
-
* against the server.
|
|
516
|
-
* 3. Call `supabase.auth.updateUser({ password })` to set the new password.
|
|
517
|
-
* 4. Update the offline credential cache with the new password hash.
|
|
518
|
-
*
|
|
519
|
-
* @param currentPassword - The user's current password (for verification).
|
|
520
|
-
* @param newPassword - The desired new password.
|
|
521
|
-
* @returns An object with an `error` field (`null` on success).
|
|
522
|
-
*
|
|
523
|
-
* @throws Never throws — all errors are returned in the `error` field.
|
|
524
|
-
*
|
|
525
|
-
* @example
|
|
526
|
-
* ```ts
|
|
527
|
-
* const { error } = await changePassword('oldPass', 'newPass');
|
|
528
|
-
* if (error) { alert(error); }
|
|
529
|
-
* ```
|
|
530
|
-
*
|
|
531
|
-
* @see {@link hashValue} — used for local password comparison
|
|
532
|
-
* @see {@link updateOfflineCredentialsPassword} — keeps the offline cache in sync
|
|
533
|
-
*/
|
|
534
|
-
export async function changePassword(currentPassword, newPassword) {
|
|
535
|
-
// Get current user email from session
|
|
536
|
-
const { data: { session } } = await supabase.auth.getSession();
|
|
537
|
-
const user = session?.user;
|
|
538
|
-
if (!user?.email) {
|
|
539
|
-
return { error: 'No authenticated user found' };
|
|
540
|
-
}
|
|
541
|
-
// Verify current password: prefer local hash check, fall back to Supabase
|
|
542
|
-
const creds = await getOfflineCredentials();
|
|
543
|
-
if (creds && creds.password && creds.email === user.email) {
|
|
544
|
-
/* Local verification path — compare against the cached credential hash.
|
|
545
|
-
This avoids an extra network call and works even in degraded-connectivity
|
|
546
|
-
scenarios. We must handle both hashed and (legacy) plaintext caches. */
|
|
547
|
-
let passwordMatch;
|
|
548
|
-
if (isAlreadyHashed(creds.password)) {
|
|
549
|
-
const hashedInput = await hashValue(currentPassword);
|
|
550
|
-
passwordMatch = creds.password === hashedInput;
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
passwordMatch = creds.password === currentPassword;
|
|
554
|
-
}
|
|
555
|
-
if (!passwordMatch) {
|
|
556
|
-
return { error: 'Current password is incorrect' };
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
else {
|
|
560
|
-
/* No usable cached credentials — fall back to a full Supabase
|
|
561
|
-
re-authentication call to verify the current password. */
|
|
562
|
-
const { error: verifyError } = await supabase.auth.signInWithPassword({
|
|
563
|
-
email: user.email,
|
|
564
|
-
password: currentPassword
|
|
565
|
-
});
|
|
566
|
-
if (verifyError) {
|
|
567
|
-
return { error: 'Current password is incorrect' };
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
// Update password
|
|
571
|
-
const { error } = await supabase.auth.updateUser({
|
|
572
|
-
password: newPassword
|
|
573
|
-
});
|
|
574
|
-
if (!error) {
|
|
575
|
-
// Update offline cache with new password
|
|
576
|
-
try {
|
|
577
|
-
await updateOfflineCredentialsPassword(newPassword);
|
|
578
|
-
}
|
|
579
|
-
catch (e) {
|
|
580
|
-
debugError('[Auth] Failed to update offline password:', e);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return { error: error?.message || null };
|
|
584
|
-
}
|
|
585
|
-
// =============================================================================
|
|
586
|
-
// SECTION: Email Management
|
|
587
|
-
// =============================================================================
|
|
588
|
-
/**
|
|
589
|
-
* Initiate an email change for the current user.
|
|
590
|
-
*
|
|
591
|
-
* Supabase sends a confirmation link to the **new** email address. The
|
|
592
|
-
* change is not applied until the user clicks that link and the app calls
|
|
593
|
-
* {@link completeEmailChange}.
|
|
594
|
-
*
|
|
595
|
-
* @param newEmail - The desired new email address.
|
|
596
|
-
* @returns An object indicating whether a confirmation email was sent, plus
|
|
597
|
-
* any error that occurred.
|
|
598
|
-
*
|
|
599
|
-
* @example
|
|
600
|
-
* ```ts
|
|
601
|
-
* const { error, confirmationRequired } = await changeEmail('new@example.com');
|
|
602
|
-
* if (confirmationRequired) {
|
|
603
|
-
* // Tell the user to check their inbox
|
|
604
|
-
* }
|
|
605
|
-
* ```
|
|
606
|
-
*
|
|
607
|
-
* @see {@link completeEmailChange} — finishes the flow after confirmation
|
|
608
|
-
*/
|
|
609
|
-
export async function changeEmail(newEmail) {
|
|
610
|
-
const { error } = await supabase.auth.updateUser({ email: newEmail });
|
|
611
|
-
if (error) {
|
|
612
|
-
return { error: error.message, confirmationRequired: false };
|
|
613
|
-
}
|
|
614
|
-
return { error: null, confirmationRequired: true };
|
|
615
|
-
}
|
|
616
|
-
/**
|
|
617
|
-
* Complete an email change after the user confirms via the email link.
|
|
618
|
-
*
|
|
619
|
-
* Refreshes the Supabase session to pick up the updated email address,
|
|
620
|
-
* then updates the offline credential cache so that offline login uses
|
|
621
|
-
* the new email.
|
|
622
|
-
*
|
|
623
|
-
* @returns An object containing the new email and/or an error message.
|
|
624
|
-
*
|
|
625
|
-
* @example
|
|
626
|
-
* ```ts
|
|
627
|
-
* const { error, newEmail } = await completeEmailChange();
|
|
628
|
-
* if (!error) {
|
|
629
|
-
* console.log(`Email changed to ${newEmail}`);
|
|
630
|
-
* }
|
|
631
|
-
* ```
|
|
632
|
-
*
|
|
633
|
-
* @see {@link changeEmail} — initiates the flow
|
|
634
|
-
*/
|
|
635
|
-
export async function completeEmailChange() {
|
|
636
|
-
const { data, error: refreshError } = await supabase.auth.refreshSession();
|
|
637
|
-
if (refreshError || !data.session) {
|
|
638
|
-
return { error: refreshError?.message || 'Failed to refresh session', newEmail: null };
|
|
639
|
-
}
|
|
640
|
-
const newEmail = data.session.user.email;
|
|
641
|
-
if (!newEmail) {
|
|
642
|
-
return { error: 'No email found in updated session', newEmail: null };
|
|
643
|
-
}
|
|
644
|
-
// Update offline credentials cache
|
|
645
|
-
try {
|
|
646
|
-
const db = getEngineConfig().db;
|
|
647
|
-
if (db) {
|
|
648
|
-
const existing = await db.table('offlineCredentials').get('current_user');
|
|
649
|
-
if (existing) {
|
|
650
|
-
await db.table('offlineCredentials').update('current_user', {
|
|
651
|
-
email: newEmail,
|
|
652
|
-
cachedAt: new Date().toISOString()
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
catch (e) {
|
|
658
|
-
debugError('[Auth] Failed to update offline credentials after email change:', e);
|
|
659
|
-
}
|
|
660
|
-
return { error: null, newEmail };
|
|
661
|
-
}
|
|
662
|
-
// =============================================================================
|
|
663
363
|
// SECTION: Email Confirmation & OTP
|
|
664
364
|
// =============================================================================
|
|
665
365
|
/**
|