@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.
Files changed (70) hide show
  1. package/README.md +19 -26
  2. package/dist/auth/crypto.d.ts +0 -23
  3. package/dist/auth/crypto.d.ts.map +1 -1
  4. package/dist/auth/crypto.js +0 -25
  5. package/dist/auth/crypto.js.map +1 -1
  6. package/dist/auth/deviceVerification.d.ts +2 -2
  7. package/dist/auth/deviceVerification.js +2 -2
  8. package/dist/auth/loginGuard.d.ts +7 -14
  9. package/dist/auth/loginGuard.d.ts.map +1 -1
  10. package/dist/auth/loginGuard.js +27 -62
  11. package/dist/auth/loginGuard.js.map +1 -1
  12. package/dist/auth/offlineCredentials.d.ts +6 -59
  13. package/dist/auth/offlineCredentials.d.ts.map +1 -1
  14. package/dist/auth/offlineCredentials.js +8 -111
  15. package/dist/auth/offlineCredentials.js.map +1 -1
  16. package/dist/auth/resolveAuthState.d.ts +14 -18
  17. package/dist/auth/resolveAuthState.d.ts.map +1 -1
  18. package/dist/auth/resolveAuthState.js +16 -58
  19. package/dist/auth/resolveAuthState.js.map +1 -1
  20. package/dist/auth/singleUser.js +4 -4
  21. package/dist/auth/singleUser.js.map +1 -1
  22. package/dist/bin/install-pwa.d.ts +4 -2
  23. package/dist/bin/install-pwa.d.ts.map +1 -1
  24. package/dist/bin/install-pwa.js +2289 -249
  25. package/dist/bin/install-pwa.js.map +1 -1
  26. package/dist/config.d.ts +3 -7
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js +1 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/data.d.ts +105 -0
  31. package/dist/data.d.ts.map +1 -1
  32. package/dist/data.js +126 -0
  33. package/dist/data.js.map +1 -1
  34. package/dist/entries/auth.d.ts +8 -13
  35. package/dist/entries/auth.d.ts.map +1 -1
  36. package/dist/entries/auth.js +11 -40
  37. package/dist/entries/auth.js.map +1 -1
  38. package/dist/entries/stores.d.ts +2 -0
  39. package/dist/entries/stores.d.ts.map +1 -1
  40. package/dist/entries/stores.js +7 -0
  41. package/dist/entries/stores.js.map +1 -1
  42. package/dist/entries/types.d.ts +0 -1
  43. package/dist/entries/types.d.ts.map +1 -1
  44. package/dist/index.d.ts +6 -6
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +21 -18
  47. package/dist/index.js.map +1 -1
  48. package/dist/kit/loads.d.ts +3 -5
  49. package/dist/kit/loads.d.ts.map +1 -1
  50. package/dist/kit/loads.js +2 -10
  51. package/dist/kit/loads.js.map +1 -1
  52. package/dist/stores/authState.d.ts +2 -2
  53. package/dist/stores/authState.js +1 -1
  54. package/dist/stores/factories.d.ts +138 -0
  55. package/dist/stores/factories.d.ts.map +1 -0
  56. package/dist/stores/factories.js +154 -0
  57. package/dist/stores/factories.js.map +1 -0
  58. package/dist/supabase/auth.d.ts +14 -181
  59. package/dist/supabase/auth.d.ts.map +1 -1
  60. package/dist/supabase/auth.js +17 -317
  61. package/dist/supabase/auth.js.map +1 -1
  62. package/package.json +1 -1
  63. package/dist/auth/admin.d.ts +0 -49
  64. package/dist/auth/admin.d.ts.map +0 -1
  65. package/dist/auth/admin.js +0 -66
  66. package/dist/auth/admin.js.map +0 -1
  67. package/dist/auth/offlineLogin.d.ts +0 -120
  68. package/dist/auth/offlineLogin.d.ts.map +0 -1
  69. package/dist/auth/offlineLogin.js +0 -142
  70. package/dist/auth/offlineLogin.js.map +0 -1
@@ -1,36 +1,31 @@
1
1
  /**
2
2
  * @fileoverview Supabase Authentication Module
3
3
  *
4
- * Provides a complete authentication layer on top of Supabase Auth, with
5
- * built-in support for:
4
+ * Provides core authentication utilities on top of Supabase Auth for the
5
+ * single-user PIN/password gate system:
6
6
  *
7
- * - **Offline credential caching**: On successful login, credentials are hashed
8
- * and persisted locally so that users can re-authenticate even when the device
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
- * - **Login guard / brute-force protection**: Every sign-in attempt passes
12
- * through a local pre-check (`loginGuard`) that enforces rate-limiting and
13
- * multi-user strategy rules *before* hitting the Supabase API.
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
- * - **Device verification (optional)**: When enabled in the engine config, an
16
- * untrusted device will trigger an OTP flow and the user will not receive a
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
- * - **Graceful session recovery**: `getSession()` falls back to localStorage
20
- * when the device is offline, ensuring the app can still render authenticated
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
- * - Consumed by UI auth screens (login, signup, profile, password change).
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 (useful for "switch account" scenarios).
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAgD3D;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAElB,+FAA+F;IAC/F,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAExB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAErB;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkEnF;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,OAAO,CAAC,YAAY,CAAC,CAoBvB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,cAAc,CAClC,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAsDnC;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,oBAAoB,EAAE,OAAO,CAAA;CAAE,CAAC,CAQlE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC,CA4BD;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"}
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"}
@@ -1,36 +1,31 @@
1
1
  /**
2
2
  * @fileoverview Supabase Authentication Module
3
3
  *
4
- * Provides a complete authentication layer on top of Supabase Auth, with
5
- * built-in support for:
4
+ * Provides core authentication utilities on top of Supabase Auth for the
5
+ * single-user PIN/password gate system:
6
6
  *
7
- * - **Offline credential caching**: On successful login, credentials are hashed
8
- * and persisted locally so that users can re-authenticate even when the device
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
- * - **Login guard / brute-force protection**: Every sign-in attempt passes
12
- * through a local pre-check (`loginGuard`) that enforces rate-limiting and
13
- * multi-user strategy rules *before* hitting the Supabase API.
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
- * - **Device verification (optional)**: When enabled in the engine config, an
16
- * untrusted device will trigger an OTP flow and the user will not receive a
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
- * - **Graceful session recovery**: `getSession()` falls back to localStorage
20
- * when the device is offline, ensuring the app can still render authenticated
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
- * - Consumed by UI auth screens (login, signup, profile, password change).
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 { cacheOfflineCredentials, clearOfflineCredentials, getOfflineCredentials, updateOfflineCredentialsPassword, updateOfflineCredentialsProfile } from '../auth/offlineCredentials';
36
+ import { clearOfflineCredentials, updateOfflineCredentialsProfile } from '../auth/offlineCredentials';
42
37
  import { clearOfflineSession } from '../auth/offlineSession';
43
- import { preCheckLogin, onLoginSuccess, onLoginFailure, resetLoginGuard } from '../auth/loginGuard';
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 signUp} — uses this URL as `emailRedirectTo`
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 (useful for "switch account" scenarios).
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
  /**