@prabhask5/stellar-engine 1.1.6 → 1.1.8

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 (265) hide show
  1. package/README.md +68 -25
  2. package/dist/actions/remoteChange.d.ts +143 -18
  3. package/dist/actions/remoteChange.d.ts.map +1 -1
  4. package/dist/actions/remoteChange.js +182 -58
  5. package/dist/actions/remoteChange.js.map +1 -1
  6. package/dist/actions/truncateTooltip.d.ts +56 -0
  7. package/dist/actions/truncateTooltip.d.ts.map +1 -0
  8. package/dist/actions/truncateTooltip.js +312 -0
  9. package/dist/actions/truncateTooltip.js.map +1 -0
  10. package/dist/auth/admin.d.ts +40 -3
  11. package/dist/auth/admin.d.ts.map +1 -1
  12. package/dist/auth/admin.js +45 -5
  13. package/dist/auth/admin.js.map +1 -1
  14. package/dist/auth/crypto.d.ts +55 -5
  15. package/dist/auth/crypto.d.ts.map +1 -1
  16. package/dist/auth/crypto.js +58 -5
  17. package/dist/auth/crypto.js.map +1 -1
  18. package/dist/auth/deviceVerification.d.ts +236 -20
  19. package/dist/auth/deviceVerification.d.ts.map +1 -1
  20. package/dist/auth/deviceVerification.js +293 -40
  21. package/dist/auth/deviceVerification.js.map +1 -1
  22. package/dist/auth/displayUtils.d.ts +98 -0
  23. package/dist/auth/displayUtils.d.ts.map +1 -0
  24. package/dist/auth/displayUtils.js +133 -0
  25. package/dist/auth/displayUtils.js.map +1 -0
  26. package/dist/auth/loginGuard.d.ts +108 -14
  27. package/dist/auth/loginGuard.d.ts.map +1 -1
  28. package/dist/auth/loginGuard.js +153 -31
  29. package/dist/auth/loginGuard.js.map +1 -1
  30. package/dist/auth/offlineCredentials.d.ts +132 -15
  31. package/dist/auth/offlineCredentials.d.ts.map +1 -1
  32. package/dist/auth/offlineCredentials.js +167 -23
  33. package/dist/auth/offlineCredentials.js.map +1 -1
  34. package/dist/auth/offlineLogin.d.ts +96 -10
  35. package/dist/auth/offlineLogin.d.ts.map +1 -1
  36. package/dist/auth/offlineLogin.js +82 -15
  37. package/dist/auth/offlineLogin.js.map +1 -1
  38. package/dist/auth/offlineSession.d.ts +83 -9
  39. package/dist/auth/offlineSession.d.ts.map +1 -1
  40. package/dist/auth/offlineSession.js +104 -13
  41. package/dist/auth/offlineSession.js.map +1 -1
  42. package/dist/auth/resolveAuthState.d.ts +70 -8
  43. package/dist/auth/resolveAuthState.d.ts.map +1 -1
  44. package/dist/auth/resolveAuthState.js +142 -46
  45. package/dist/auth/resolveAuthState.js.map +1 -1
  46. package/dist/auth/singleUser.d.ts +390 -37
  47. package/dist/auth/singleUser.d.ts.map +1 -1
  48. package/dist/auth/singleUser.js +505 -133
  49. package/dist/auth/singleUser.js.map +1 -1
  50. package/dist/bin/install-pwa.d.ts +25 -0
  51. package/dist/bin/install-pwa.d.ts.map +1 -0
  52. package/dist/bin/install-pwa.js +2197 -0
  53. package/dist/bin/install-pwa.js.map +1 -0
  54. package/dist/config.d.ts +132 -12
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +87 -9
  57. package/dist/config.js.map +1 -1
  58. package/dist/conflicts.d.ts +246 -23
  59. package/dist/conflicts.d.ts.map +1 -1
  60. package/dist/conflicts.js +495 -46
  61. package/dist/conflicts.js.map +1 -1
  62. package/dist/data.d.ts +338 -18
  63. package/dist/data.d.ts.map +1 -1
  64. package/dist/data.js +385 -34
  65. package/dist/data.js.map +1 -1
  66. package/dist/database.d.ts +72 -14
  67. package/dist/database.d.ts.map +1 -1
  68. package/dist/database.js +120 -29
  69. package/dist/database.js.map +1 -1
  70. package/dist/debug.d.ts +77 -1
  71. package/dist/debug.d.ts.map +1 -1
  72. package/dist/debug.js +88 -1
  73. package/dist/debug.js.map +1 -1
  74. package/dist/deviceId.d.ts +38 -7
  75. package/dist/deviceId.d.ts.map +1 -1
  76. package/dist/deviceId.js +68 -10
  77. package/dist/deviceId.js.map +1 -1
  78. package/dist/engine.d.ts +175 -3
  79. package/dist/engine.d.ts.map +1 -1
  80. package/dist/engine.js +831 -110
  81. package/dist/engine.js.map +1 -1
  82. package/dist/entries/actions.d.ts +14 -0
  83. package/dist/entries/actions.d.ts.map +1 -1
  84. package/dist/entries/actions.js +27 -1
  85. package/dist/entries/actions.js.map +1 -1
  86. package/dist/entries/auth.d.ts +16 -0
  87. package/dist/entries/auth.d.ts.map +1 -1
  88. package/dist/entries/auth.js +73 -1
  89. package/dist/entries/auth.js.map +1 -1
  90. package/dist/entries/config.d.ts +12 -0
  91. package/dist/entries/config.d.ts.map +1 -1
  92. package/dist/entries/config.js +18 -1
  93. package/dist/entries/config.js.map +1 -1
  94. package/dist/entries/kit.d.ts +21 -9
  95. package/dist/entries/kit.d.ts.map +1 -1
  96. package/dist/entries/kit.js +57 -8
  97. package/dist/entries/kit.js.map +1 -1
  98. package/dist/entries/stores.d.ts +11 -0
  99. package/dist/entries/stores.d.ts.map +1 -1
  100. package/dist/entries/stores.js +43 -2
  101. package/dist/entries/stores.js.map +1 -1
  102. package/dist/entries/types.d.ts +11 -1
  103. package/dist/entries/types.d.ts.map +1 -1
  104. package/dist/entries/types.js +10 -0
  105. package/dist/entries/types.js.map +1 -1
  106. package/dist/entries/utils.d.ts +7 -1
  107. package/dist/entries/utils.d.ts.map +1 -1
  108. package/dist/entries/utils.js +23 -2
  109. package/dist/entries/utils.js.map +1 -1
  110. package/dist/entries/vite.d.ts +20 -0
  111. package/dist/entries/vite.d.ts.map +1 -0
  112. package/dist/entries/vite.js +26 -0
  113. package/dist/entries/vite.js.map +1 -0
  114. package/dist/index.d.ts +33 -2
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +176 -21
  117. package/dist/index.js.map +1 -1
  118. package/dist/kit/auth.d.ts +80 -0
  119. package/dist/kit/auth.d.ts.map +1 -0
  120. package/dist/kit/auth.js +72 -0
  121. package/dist/kit/auth.js.map +1 -0
  122. package/dist/kit/confirm.d.ts +111 -0
  123. package/dist/kit/confirm.d.ts.map +1 -0
  124. package/dist/kit/confirm.js +169 -0
  125. package/dist/kit/confirm.js.map +1 -0
  126. package/dist/kit/loads.d.ts +189 -0
  127. package/dist/kit/loads.d.ts.map +1 -0
  128. package/dist/kit/loads.js +205 -0
  129. package/dist/kit/loads.js.map +1 -0
  130. package/dist/kit/server.d.ts +175 -0
  131. package/dist/kit/server.d.ts.map +1 -0
  132. package/dist/kit/server.js +297 -0
  133. package/dist/kit/server.js.map +1 -0
  134. package/dist/kit/sw.d.ts +176 -0
  135. package/dist/kit/sw.d.ts.map +1 -0
  136. package/dist/kit/sw.js +320 -0
  137. package/dist/kit/sw.js.map +1 -0
  138. package/dist/queue.d.ts +274 -0
  139. package/dist/queue.d.ts.map +1 -1
  140. package/dist/queue.js +556 -38
  141. package/dist/queue.js.map +1 -1
  142. package/dist/realtime.d.ts +241 -27
  143. package/dist/realtime.d.ts.map +1 -1
  144. package/dist/realtime.js +633 -109
  145. package/dist/realtime.js.map +1 -1
  146. package/dist/runtime/runtimeConfig.d.ts +91 -16
  147. package/dist/runtime/runtimeConfig.d.ts.map +1 -1
  148. package/dist/runtime/runtimeConfig.js +146 -19
  149. package/dist/runtime/runtimeConfig.js.map +1 -1
  150. package/dist/stores/authState.d.ts +150 -11
  151. package/dist/stores/authState.d.ts.map +1 -1
  152. package/dist/stores/authState.js +169 -17
  153. package/dist/stores/authState.js.map +1 -1
  154. package/dist/stores/network.d.ts +39 -0
  155. package/dist/stores/network.d.ts.map +1 -1
  156. package/dist/stores/network.js +169 -16
  157. package/dist/stores/network.js.map +1 -1
  158. package/dist/stores/remoteChanges.d.ts +327 -52
  159. package/dist/stores/remoteChanges.d.ts.map +1 -1
  160. package/dist/stores/remoteChanges.js +337 -75
  161. package/dist/stores/remoteChanges.js.map +1 -1
  162. package/dist/stores/sync.d.ts +130 -0
  163. package/dist/stores/sync.d.ts.map +1 -1
  164. package/dist/stores/sync.js +167 -7
  165. package/dist/stores/sync.js.map +1 -1
  166. package/dist/supabase/auth.d.ts +326 -19
  167. package/dist/supabase/auth.d.ts.map +1 -1
  168. package/dist/supabase/auth.js +374 -26
  169. package/dist/supabase/auth.js.map +1 -1
  170. package/dist/supabase/client.d.ts +79 -6
  171. package/dist/supabase/client.d.ts.map +1 -1
  172. package/dist/supabase/client.js +158 -15
  173. package/dist/supabase/client.js.map +1 -1
  174. package/dist/supabase/validate.d.ts +101 -7
  175. package/dist/supabase/validate.d.ts.map +1 -1
  176. package/dist/supabase/validate.js +117 -8
  177. package/dist/supabase/validate.js.map +1 -1
  178. package/dist/sw/build/vite-plugin.d.ts +74 -0
  179. package/dist/sw/build/vite-plugin.d.ts.map +1 -0
  180. package/dist/sw/build/vite-plugin.js +183 -0
  181. package/dist/sw/build/vite-plugin.js.map +1 -0
  182. package/dist/sw/sw.js +669 -0
  183. package/dist/types.d.ts +150 -45
  184. package/dist/types.d.ts.map +1 -1
  185. package/dist/types.js +12 -10
  186. package/dist/types.js.map +1 -1
  187. package/dist/utils.d.ts +55 -13
  188. package/dist/utils.d.ts.map +1 -1
  189. package/dist/utils.js +83 -22
  190. package/dist/utils.js.map +1 -1
  191. package/package.json +20 -22
  192. package/src/components/DeferredChangesBanner.svelte +477 -0
  193. package/src/components/SyncStatus.svelte +1732 -0
  194. package/dist/crdt/awareness.d.ts +0 -54
  195. package/dist/crdt/awareness.d.ts.map +0 -1
  196. package/dist/crdt/awareness.js +0 -219
  197. package/dist/crdt/awareness.js.map +0 -1
  198. package/dist/crdt/doc.d.ts +0 -56
  199. package/dist/crdt/doc.d.ts.map +0 -1
  200. package/dist/crdt/doc.js +0 -130
  201. package/dist/crdt/doc.js.map +0 -1
  202. package/dist/crdt/index.d.ts +0 -15
  203. package/dist/crdt/index.d.ts.map +0 -1
  204. package/dist/crdt/index.js +0 -20
  205. package/dist/crdt/index.js.map +0 -1
  206. package/dist/crdt/offline.d.ts +0 -91
  207. package/dist/crdt/offline.d.ts.map +0 -1
  208. package/dist/crdt/offline.js +0 -353
  209. package/dist/crdt/offline.js.map +0 -1
  210. package/dist/crdt/sync.d.ts +0 -58
  211. package/dist/crdt/sync.d.ts.map +0 -1
  212. package/dist/crdt/sync.js +0 -399
  213. package/dist/crdt/sync.js.map +0 -1
  214. package/dist/crdt/types.d.ts +0 -62
  215. package/dist/crdt/types.d.ts.map +0 -1
  216. package/dist/crdt/types.js +0 -7
  217. package/dist/crdt/types.js.map +0 -1
  218. package/dist/email/sendEmail.d.ts +0 -31
  219. package/dist/email/sendEmail.d.ts.map +0 -1
  220. package/dist/email/sendEmail.js +0 -39
  221. package/dist/email/sendEmail.js.map +0 -1
  222. package/dist/email/validateSmtp.d.ts +0 -18
  223. package/dist/email/validateSmtp.d.ts.map +0 -1
  224. package/dist/email/validateSmtp.js +0 -33
  225. package/dist/email/validateSmtp.js.map +0 -1
  226. package/dist/entries/crdt.d.ts +0 -3
  227. package/dist/entries/crdt.d.ts.map +0 -1
  228. package/dist/entries/crdt.js +0 -13
  229. package/dist/entries/crdt.js.map +0 -1
  230. package/dist/entries/email.d.ts +0 -4
  231. package/dist/entries/email.d.ts.map +0 -1
  232. package/dist/entries/email.js +0 -4
  233. package/dist/entries/email.js.map +0 -1
  234. package/dist/kit/authPresets.d.ts +0 -28
  235. package/dist/kit/authPresets.d.ts.map +0 -1
  236. package/dist/kit/authPresets.js +0 -23
  237. package/dist/kit/authPresets.js.map +0 -1
  238. package/dist/kit/configEndpoint.d.ts +0 -18
  239. package/dist/kit/configEndpoint.d.ts.map +0 -1
  240. package/dist/kit/configEndpoint.js +0 -27
  241. package/dist/kit/configEndpoint.js.map +0 -1
  242. package/dist/kit/deployEndpoint.d.ts +0 -22
  243. package/dist/kit/deployEndpoint.d.ts.map +0 -1
  244. package/dist/kit/deployEndpoint.js +0 -79
  245. package/dist/kit/deployEndpoint.js.map +0 -1
  246. package/dist/kit/layoutLoad.d.ts +0 -23
  247. package/dist/kit/layoutLoad.d.ts.map +0 -1
  248. package/dist/kit/layoutLoad.js +0 -41
  249. package/dist/kit/layoutLoad.js.map +0 -1
  250. package/dist/kit/protectedLoad.d.ts +0 -16
  251. package/dist/kit/protectedLoad.d.ts.map +0 -1
  252. package/dist/kit/protectedLoad.js +0 -28
  253. package/dist/kit/protectedLoad.js.map +0 -1
  254. package/dist/kit/setupLoad.d.ts +0 -11
  255. package/dist/kit/setupLoad.d.ts.map +0 -1
  256. package/dist/kit/setupLoad.js +0 -28
  257. package/dist/kit/setupLoad.js.map +0 -1
  258. package/dist/kit/validateEndpoint.d.ts +0 -9
  259. package/dist/kit/validateEndpoint.d.ts.map +0 -1
  260. package/dist/kit/validateEndpoint.js +0 -25
  261. package/dist/kit/validateEndpoint.js.map +0 -1
  262. package/dist/kit/vercelApi.d.ts +0 -6
  263. package/dist/kit/vercelApi.d.ts.map +0 -1
  264. package/dist/kit/vercelApi.js +0 -48
  265. package/dist/kit/vercelApi.js.map +0 -1
@@ -1,41 +1,158 @@
1
1
  /**
2
- * Offline Credentials Management
3
- * Handles caching, retrieval, and verification of user credentials for offline login
2
+ * @fileoverview Offline Credentials Management
3
+ *
4
+ * Handles caching, retrieval, verification, and update of user credentials
5
+ * in IndexedDB for offline login support. When the user successfully
6
+ * authenticates online via Supabase, their credentials are cached locally
7
+ * (with the password SHA-256-hashed) so they can log in again while offline.
8
+ *
9
+ * Architecture:
10
+ * - Credentials are stored as a singleton record (key: `'current_user'`) in the
11
+ * `offlineCredentials` IndexedDB table.
12
+ * - Only one set of credentials is cached at a time. In multi-user apps, the
13
+ * last successfully authenticated user's credentials are stored.
14
+ * - The profile blob is extracted via the host app's `profileExtractor` config
15
+ * callback, or falls back to raw Supabase `user_metadata`.
16
+ *
17
+ * Security considerations:
18
+ * - Passwords are **always** hashed with SHA-256 before storage. The plaintext
19
+ * password is never persisted.
20
+ * - Legacy plaintext credentials (from before the hashing migration) are
21
+ * supported in `verifyOfflineCredentials` via the `isAlreadyHashed` check,
22
+ * but new writes always hash.
23
+ * - A paranoid read-back verification is performed after `cacheOfflineCredentials`
24
+ * to ensure the password was actually persisted (guards against silent
25
+ * IndexedDB write failures).
26
+ * - Credentials are cleared on logout via `clearOfflineCredentials`.
27
+ *
28
+ * @module auth/offlineCredentials
4
29
  */
5
30
  import type { OfflineCredentials } from '../types';
6
31
  import type { User, Session } from '@supabase/supabase-js';
7
32
  /**
8
- * Cache user credentials for offline login
9
- * Called after successful Supabase login
33
+ * Cache user credentials for offline login.
34
+ *
35
+ * Called after a successful Supabase login to persist a hashed copy of the
36
+ * user's credentials in IndexedDB. Subsequent offline logins will verify
37
+ * against these cached credentials.
38
+ *
39
+ * @param email - The user's email address (used for offline identity matching).
40
+ * @param password - The user's plaintext password. Will be SHA-256-hashed before storage.
41
+ * @param user - The Supabase `User` object, used to extract `userId` and profile data.
42
+ * @param _session - The Supabase `Session` object. Currently unused but accepted for
43
+ * API symmetry with the online auth flow (reserved for future use).
44
+ *
45
+ * @throws {Error} If `email` or `password` is empty (prevents storing incomplete credentials).
46
+ * @throws {Error} If the write-back verification fails (password not persisted in IndexedDB).
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const { data } = await supabase.auth.signInWithPassword({ email, password });
51
+ * if (data.user && data.session) {
52
+ * await cacheOfflineCredentials(email, password, data.user, data.session);
53
+ * }
54
+ * ```
55
+ *
56
+ * @see {@link getOfflineCredentials} to retrieve the cached credentials.
57
+ * @see {@link clearOfflineCredentials} to remove them on logout.
10
58
  */
11
59
  export declare function cacheOfflineCredentials(email: string, password: string, user: User, _session: Session): Promise<void>;
12
60
  /**
13
- * Get cached offline credentials
14
- * Returns null if no credentials are cached or if credentials are in old format
61
+ * Get cached offline credentials from IndexedDB.
62
+ *
63
+ * Returns the singleton `OfflineCredentials` record, or `null` if no
64
+ * credentials have been cached (e.g., user has never logged in online
65
+ * on this device).
66
+ *
67
+ * @returns The cached credentials, or `null` if none exist.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const creds = await getOfflineCredentials();
72
+ * if (creds) {
73
+ * console.log('Cached user:', creds.email);
74
+ * }
75
+ * ```
15
76
  */
16
77
  export declare function getOfflineCredentials(): Promise<OfflineCredentials | null>;
17
78
  /**
18
- * Verify email and password against cached credentials
19
- * @param email - The email to verify
20
- * @param password - The password to verify
21
- * @param expectedUserId - The userId that the credentials should belong to
22
- * @returns Object with valid boolean and optional reason for failure
79
+ * Verify email and password against cached credentials.
80
+ *
81
+ * Performs a multi-field comparison:
82
+ * 1. Checks that cached credentials exist.
83
+ * 2. Verifies the `userId` matches (prevents cross-user attacks).
84
+ * 3. Verifies the `email` matches (prevents credential reuse across accounts).
85
+ * 4. Verifies the password by hashing the input and comparing against the
86
+ * stored hash (or plaintext for legacy records).
87
+ *
88
+ * @param email - The email to verify against cached credentials.
89
+ * @param password - The plaintext password to verify.
90
+ * @param expectedUserId - The Supabase user ID that the credentials should belong to.
91
+ * Prevents offline login with credentials cached from a
92
+ * different user.
93
+ * @returns An object with `valid: true` on success, or `valid: false` with a
94
+ * `reason` string identifying which check failed.
95
+ *
96
+ * @example
97
+ * ```ts
98
+ * const result = await verifyOfflineCredentials(email, password, userId);
99
+ * if (result.valid) {
100
+ * await createOfflineSession(userId);
101
+ * } else {
102
+ * console.warn('Verification failed:', result.reason);
103
+ * }
104
+ * ```
105
+ *
106
+ * @see {@link cacheOfflineCredentials} for how credentials are stored.
23
107
  */
24
108
  export declare function verifyOfflineCredentials(email: string, password: string, expectedUserId: string): Promise<{
25
109
  valid: boolean;
26
110
  reason?: string;
27
111
  }>;
28
112
  /**
29
- * Update the cached password (after online password change)
30
- * @param newPassword - The new password to cache
113
+ * Update the cached password hash after an online password change.
114
+ *
115
+ * Should be called whenever the user changes their password while online,
116
+ * so the offline cache stays in sync and the login guard does not flag
117
+ * the old hash as stale.
118
+ *
119
+ * @param newPassword - The new plaintext password. Will be SHA-256-hashed before storage.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * await supabase.auth.updateUser({ password: newPassword });
124
+ * await updateOfflineCredentialsPassword(newPassword);
125
+ * ```
31
126
  */
32
127
  export declare function updateOfflineCredentialsPassword(newPassword: string): Promise<void>;
33
128
  /**
34
- * Update user profile in cached credentials (after online profile update)
129
+ * Update the user profile in cached credentials after an online profile update.
130
+ *
131
+ * Replaces the entire `profile` blob with the provided object and updates
132
+ * the `cachedAt` timestamp.
133
+ *
134
+ * @param profile - The new profile data to cache (e.g., `{ firstName, lastName, avatar }`).
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * await supabase.auth.updateUser({ data: { firstName: 'Jane' } });
139
+ * await updateOfflineCredentialsProfile({ firstName: 'Jane', lastName: 'Doe' });
140
+ * ```
35
141
  */
36
142
  export declare function updateOfflineCredentialsProfile(profile: Record<string, unknown>): Promise<void>;
37
143
  /**
38
- * Clear all cached offline credentials (on logout)
144
+ * Clear all cached offline credentials from IndexedDB.
145
+ *
146
+ * Must be called on logout to ensure no stale credentials remain on the
147
+ * device that could be used for unauthorized offline access.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * await supabase.auth.signOut();
152
+ * await clearOfflineCredentials();
153
+ * ```
154
+ *
155
+ * @see {@link cacheOfflineCredentials} for storing credentials on login.
39
156
  */
40
157
  export declare function clearOfflineCredentials(): Promise<void>;
41
158
  //# sourceMappingURL=offlineCredentials.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"offlineCredentials.d.ts","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAM3D;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAmCf;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQhF;AAED;;;;;;GAMG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8C9C;AAED;;;GAGG;AACH,wBAAsB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzF;AAED;;GAEG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;GAEG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D"}
1
+ {"version":3,"file":"offlineCredentials.d.ts","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAmB3D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAyCf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAQhF;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAkD9C;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,gCAAgC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAG7D"}
@@ -1,24 +1,86 @@
1
1
  /**
2
- * Offline Credentials Management
3
- * Handles caching, retrieval, and verification of user credentials for offline login
2
+ * @fileoverview Offline Credentials Management
3
+ *
4
+ * Handles caching, retrieval, verification, and update of user credentials
5
+ * in IndexedDB for offline login support. When the user successfully
6
+ * authenticates online via Supabase, their credentials are cached locally
7
+ * (with the password SHA-256-hashed) so they can log in again while offline.
8
+ *
9
+ * Architecture:
10
+ * - Credentials are stored as a singleton record (key: `'current_user'`) in the
11
+ * `offlineCredentials` IndexedDB table.
12
+ * - Only one set of credentials is cached at a time. In multi-user apps, the
13
+ * last successfully authenticated user's credentials are stored.
14
+ * - The profile blob is extracted via the host app's `profileExtractor` config
15
+ * callback, or falls back to raw Supabase `user_metadata`.
16
+ *
17
+ * Security considerations:
18
+ * - Passwords are **always** hashed with SHA-256 before storage. The plaintext
19
+ * password is never persisted.
20
+ * - Legacy plaintext credentials (from before the hashing migration) are
21
+ * supported in `verifyOfflineCredentials` via the `isAlreadyHashed` check,
22
+ * but new writes always hash.
23
+ * - A paranoid read-back verification is performed after `cacheOfflineCredentials`
24
+ * to ensure the password was actually persisted (guards against silent
25
+ * IndexedDB write failures).
26
+ * - Credentials are cleared on logout via `clearOfflineCredentials`.
27
+ *
28
+ * @module auth/offlineCredentials
4
29
  */
5
30
  import { getEngineConfig } from '../config';
6
31
  import { debugWarn, debugError } from '../debug';
7
32
  import { hashValue, isAlreadyHashed } from './crypto';
33
+ // =============================================================================
34
+ // CONSTANTS
35
+ // =============================================================================
36
+ /**
37
+ * Singleton key used to store and retrieve the cached credentials record
38
+ * in the `offlineCredentials` IndexedDB table. Only one credential set is
39
+ * cached at any time.
40
+ */
8
41
  const CREDENTIALS_ID = 'current_user';
42
+ // =============================================================================
43
+ // PUBLIC API -- Cache & Retrieve
44
+ // =============================================================================
9
45
  /**
10
- * Cache user credentials for offline login
11
- * Called after successful Supabase login
46
+ * Cache user credentials for offline login.
47
+ *
48
+ * Called after a successful Supabase login to persist a hashed copy of the
49
+ * user's credentials in IndexedDB. Subsequent offline logins will verify
50
+ * against these cached credentials.
51
+ *
52
+ * @param email - The user's email address (used for offline identity matching).
53
+ * @param password - The user's plaintext password. Will be SHA-256-hashed before storage.
54
+ * @param user - The Supabase `User` object, used to extract `userId` and profile data.
55
+ * @param _session - The Supabase `Session` object. Currently unused but accepted for
56
+ * API symmetry with the online auth flow (reserved for future use).
57
+ *
58
+ * @throws {Error} If `email` or `password` is empty (prevents storing incomplete credentials).
59
+ * @throws {Error} If the write-back verification fails (password not persisted in IndexedDB).
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const { data } = await supabase.auth.signInWithPassword({ email, password });
64
+ * if (data.user && data.session) {
65
+ * await cacheOfflineCredentials(email, password, data.user, data.session);
66
+ * }
67
+ * ```
68
+ *
69
+ * @see {@link getOfflineCredentials} to retrieve the cached credentials.
70
+ * @see {@link clearOfflineCredentials} to remove them on logout.
12
71
  */
13
72
  export async function cacheOfflineCredentials(email, password, user, _session) {
14
- // Validate inputs to prevent storing incomplete credentials
73
+ /* Validate inputs to prevent storing incomplete credentials that would
74
+ cause confusing verification failures later. */
15
75
  if (!email || !password) {
16
76
  debugError('[Auth] Cannot cache credentials: email or password is empty');
17
77
  throw new Error('Cannot cache credentials: email or password is empty');
18
78
  }
19
79
  const config = getEngineConfig();
20
80
  const db = config.db;
21
- // Extract profile using config's profileExtractor, or use raw metadata
81
+ /* Extract a normalized profile using the host app's profileExtractor,
82
+ or fall back to raw Supabase user_metadata. This allows the host app
83
+ to control which fields are available offline (e.g., firstName, role). */
22
84
  const profile = config.auth?.profileExtractor
23
85
  ? config.auth.profileExtractor(user.user_metadata || {})
24
86
  : user.user_metadata || {};
@@ -31,9 +93,12 @@ export async function cacheOfflineCredentials(email, password, user, _session) {
31
93
  profile,
32
94
  cachedAt: new Date().toISOString()
33
95
  };
34
- // Use put to insert or update the singleton record
96
+ /* Use put (upsert) to insert or update the singleton record. */
35
97
  await db.table('offlineCredentials').put(credentials);
36
- // Verify the credentials were stored correctly (paranoid check)
98
+ /* Paranoid read-back: verify the credentials were stored correctly.
99
+ IndexedDB writes can silently fail in quota-exceeded or private-browsing
100
+ scenarios; catching this early gives a clear error instead of a mysterious
101
+ "wrong password" on the next offline login. */
37
102
  const stored = await db.table('offlineCredentials').get(CREDENTIALS_ID);
38
103
  if (!stored || !stored.password) {
39
104
  debugError('[Auth] Credentials were not stored correctly - password missing');
@@ -41,8 +106,21 @@ export async function cacheOfflineCredentials(email, password, user, _session) {
41
106
  }
42
107
  }
43
108
  /**
44
- * Get cached offline credentials
45
- * Returns null if no credentials are cached or if credentials are in old format
109
+ * Get cached offline credentials from IndexedDB.
110
+ *
111
+ * Returns the singleton `OfflineCredentials` record, or `null` if no
112
+ * credentials have been cached (e.g., user has never logged in online
113
+ * on this device).
114
+ *
115
+ * @returns The cached credentials, or `null` if none exist.
116
+ *
117
+ * @example
118
+ * ```ts
119
+ * const creds = await getOfflineCredentials();
120
+ * if (creds) {
121
+ * console.log('Cached user:', creds.email);
122
+ * }
123
+ * ```
46
124
  */
47
125
  export async function getOfflineCredentials() {
48
126
  const db = getEngineConfig().db;
@@ -52,12 +130,38 @@ export async function getOfflineCredentials() {
52
130
  }
53
131
  return credentials;
54
132
  }
133
+ // =============================================================================
134
+ // PUBLIC API -- Verification
135
+ // =============================================================================
55
136
  /**
56
- * Verify email and password against cached credentials
57
- * @param email - The email to verify
58
- * @param password - The password to verify
59
- * @param expectedUserId - The userId that the credentials should belong to
60
- * @returns Object with valid boolean and optional reason for failure
137
+ * Verify email and password against cached credentials.
138
+ *
139
+ * Performs a multi-field comparison:
140
+ * 1. Checks that cached credentials exist.
141
+ * 2. Verifies the `userId` matches (prevents cross-user attacks).
142
+ * 3. Verifies the `email` matches (prevents credential reuse across accounts).
143
+ * 4. Verifies the password by hashing the input and comparing against the
144
+ * stored hash (or plaintext for legacy records).
145
+ *
146
+ * @param email - The email to verify against cached credentials.
147
+ * @param password - The plaintext password to verify.
148
+ * @param expectedUserId - The Supabase user ID that the credentials should belong to.
149
+ * Prevents offline login with credentials cached from a
150
+ * different user.
151
+ * @returns An object with `valid: true` on success, or `valid: false` with a
152
+ * `reason` string identifying which check failed.
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const result = await verifyOfflineCredentials(email, password, userId);
157
+ * if (result.valid) {
158
+ * await createOfflineSession(userId);
159
+ * } else {
160
+ * console.warn('Verification failed:', result.reason);
161
+ * }
162
+ * ```
163
+ *
164
+ * @see {@link cacheOfflineCredentials} for how credentials are stored.
61
165
  */
62
166
  export async function verifyOfflineCredentials(email, password, expectedUserId) {
63
167
  const credentials = await getOfflineCredentials();
@@ -65,7 +169,9 @@ export async function verifyOfflineCredentials(email, password, expectedUserId)
65
169
  debugWarn('[Auth] No credentials found in database');
66
170
  return { valid: false, reason: 'no_credentials' };
67
171
  }
68
- // SECURITY: Verify all fields match
172
+ /* SECURITY: Verify all identity fields match. Checking userId AND email
173
+ provides defense-in-depth -- even if one field is spoofed, the other
174
+ must also match. */
69
175
  if (credentials.userId !== expectedUserId) {
70
176
  debugWarn('[Auth] Credential userId mismatch:', credentials.userId, '!==', expectedUserId);
71
177
  return { valid: false, reason: 'user_mismatch' };
@@ -78,15 +184,17 @@ export async function verifyOfflineCredentials(email, password, expectedUserId)
78
184
  debugWarn('[Auth] No password stored in credentials');
79
185
  return { valid: false, reason: 'no_stored_password' };
80
186
  }
81
- // Compare passwords: if stored password is hashed, hash the input and compare;
82
- // if legacy plaintext, compare directly for backwards compatibility
187
+ /* Compare passwords: if the stored password is a 64-char hex string, it is
188
+ a SHA-256 hash -- hash the input and compare. Otherwise, treat it as a
189
+ legacy plaintext value for backward compatibility. */
83
190
  let passwordMatch;
84
191
  if (isAlreadyHashed(credentials.password)) {
85
192
  const hashedInput = await hashValue(password);
86
193
  passwordMatch = credentials.password === hashedInput;
87
194
  }
88
195
  else {
89
- // Legacy plaintext comparison
196
+ /* Legacy plaintext comparison -- only applies to credentials cached before
197
+ the SHA-256 migration. New caches always store hashed passwords. */
90
198
  passwordMatch = credentials.password === password;
91
199
  }
92
200
  if (!passwordMatch) {
@@ -95,9 +203,23 @@ export async function verifyOfflineCredentials(email, password, expectedUserId)
95
203
  }
96
204
  return { valid: true };
97
205
  }
206
+ // =============================================================================
207
+ // PUBLIC API -- Update & Clear
208
+ // =============================================================================
98
209
  /**
99
- * Update the cached password (after online password change)
100
- * @param newPassword - The new password to cache
210
+ * Update the cached password hash after an online password change.
211
+ *
212
+ * Should be called whenever the user changes their password while online,
213
+ * so the offline cache stays in sync and the login guard does not flag
214
+ * the old hash as stale.
215
+ *
216
+ * @param newPassword - The new plaintext password. Will be SHA-256-hashed before storage.
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * await supabase.auth.updateUser({ password: newPassword });
221
+ * await updateOfflineCredentialsPassword(newPassword);
222
+ * ```
101
223
  */
102
224
  export async function updateOfflineCredentialsPassword(newPassword) {
103
225
  const credentials = await getOfflineCredentials();
@@ -112,7 +234,18 @@ export async function updateOfflineCredentialsPassword(newPassword) {
112
234
  });
113
235
  }
114
236
  /**
115
- * Update user profile in cached credentials (after online profile update)
237
+ * Update the user profile in cached credentials after an online profile update.
238
+ *
239
+ * Replaces the entire `profile` blob with the provided object and updates
240
+ * the `cachedAt` timestamp.
241
+ *
242
+ * @param profile - The new profile data to cache (e.g., `{ firstName, lastName, avatar }`).
243
+ *
244
+ * @example
245
+ * ```ts
246
+ * await supabase.auth.updateUser({ data: { firstName: 'Jane' } });
247
+ * await updateOfflineCredentialsProfile({ firstName: 'Jane', lastName: 'Doe' });
248
+ * ```
116
249
  */
117
250
  export async function updateOfflineCredentialsProfile(profile) {
118
251
  const credentials = await getOfflineCredentials();
@@ -126,7 +259,18 @@ export async function updateOfflineCredentialsProfile(profile) {
126
259
  });
127
260
  }
128
261
  /**
129
- * Clear all cached offline credentials (on logout)
262
+ * Clear all cached offline credentials from IndexedDB.
263
+ *
264
+ * Must be called on logout to ensure no stale credentials remain on the
265
+ * device that could be used for unauthorized offline access.
266
+ *
267
+ * @example
268
+ * ```ts
269
+ * await supabase.auth.signOut();
270
+ * await clearOfflineCredentials();
271
+ * ```
272
+ *
273
+ * @see {@link cacheOfflineCredentials} for storing credentials on login.
130
274
  */
131
275
  export async function clearOfflineCredentials() {
132
276
  const db = getEngineConfig().db;
@@ -1 +1 @@
1
- {"version":3,"file":"offlineCredentials.js","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"offlineCredentials.js","sourceRoot":"","sources":["../../src/auth/offlineCredentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEtD,gFAAgF;AAChF,YAAY;AACZ,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,cAAc,GAAG,cAAc,CAAC;AAEtC,gFAAgF;AAChF,iCAAiC;AACjC,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,KAAa,EACb,QAAgB,EAChB,IAAU,EACV,QAAiB;IAEjB;sDACkD;IAClD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAC;QACxB,UAAU,CAAC,6DAA6D,CAAC,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IACjC,MAAM,EAAE,GAAG,MAAM,CAAC,EAAG,CAAC;IAEtB;;gFAE4E;IAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,gBAAgB;QAC3C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;QACxD,CAAC,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,CAAC;IAE7B,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;IAEjD,MAAM,WAAW,GAAuB;QACtC,EAAE,EAAE,cAAc;QAClB,MAAM,EAAE,IAAI,CAAC,EAAE;QACf,KAAK,EAAE,KAAK;QACZ,QAAQ,EAAE,cAAc;QACxB,OAAO;QACP,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC;IAEF,gEAAgE;IAChE,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAEtD;;;qDAGiD;IACjD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACxE,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,UAAU,CAAC,iEAAiE,CAAC,CAAC;QAC9E,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC,EAAG,CAAC;IACjC,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7E,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,WAAiC,CAAC;AAC3C,CAAC;AAED,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAa,EACb,QAAgB,EAChB,cAAsB;IAEtB,MAAM,WAAW,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAClD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,SAAS,CAAC,yCAAyC,CAAC,CAAC;QACrD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACpD,CAAC;IAED;;0BAEsB;IACtB,IAAI,WAAW,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QAC1C,SAAS,CAAC,oCAAoC,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;QAC3F,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IACnD,CAAC;IAED,IAAI,WAAW,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAChC,SAAS,CAAC,mCAAmC,EAAE,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAChF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC1B,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACtD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IACxD,CAAC;IAED;;4DAEwD;IACxD,IAAI,aAAsB,CAAC;IAC3B,IAAI,eAAe,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9C,aAAa,GAAG,WAAW,CAAC,QAAQ,KAAK,WAAW,CAAC;IACvD,CAAC;SAAM,CAAC;QACN;8EACsE;QACtE,aAAa,GAAG,WAAW,CAAC,QAAQ,KAAK,QAAQ,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,SAAS,CACP,0CAA0C,EAC1C,WAAW,CAAC,QAAQ,CAAC,MAAM,EAC3B,mBAAmB,EACnB,QAAQ,CAAC,MAAM,EACf,GAAG,CACJ,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IACvD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAEhF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,WAAmB;IACxE,MAAM,WAAW,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAClD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC,EAAG,CAAC;IACjC,MAAM,cAAc,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE;QAC1D,QAAQ,EAAE,cAAc;QACxB,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,+BAA+B,CACnD,OAAgC;IAEhC,MAAM,WAAW,GAAG,MAAM,qBAAqB,EAAE,CAAC;IAClD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO;IACT,CAAC;IAED,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC,EAAG,CAAC;IACjC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,cAAc,EAAE;QAC1D,OAAO;QACP,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC,EAAG,CAAC;IACjC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;AAC9D,CAAC"}
@@ -1,33 +1,119 @@
1
1
  /**
2
- * Offline Login
2
+ * @fileoverview Offline Login Orchestrator
3
3
  *
4
- * Provides high-level offline sign-in and credential info functions,
5
- * absorbing the login page's direct use of offline auth internals.
4
+ * Provides high-level offline sign-in and credential info functions, serving as
5
+ * the primary entry point for offline authentication. This module absorbs the
6
+ * login page's direct use of offline auth internals, offering a clean API that
7
+ * coordinates credential retrieval, verification, and session creation.
8
+ *
9
+ * Architecture:
10
+ * - `signInOffline` is the main flow: fetch cached credentials, verify them,
11
+ * create an offline session, and validate it was persisted.
12
+ * - `getOfflineLoginInfo` is a read-only helper that exposes non-sensitive
13
+ * display data (email, name) for pre-populating the offline login UI.
14
+ * - Both functions are designed to never throw -- they return structured result
15
+ * objects with typed error reasons for the caller to handle.
16
+ *
17
+ * Security considerations:
18
+ * - Offline login is inherently less secure than online Supabase authentication.
19
+ * It relies on SHA-256-hashed credentials stored in IndexedDB, which a
20
+ * sufficiently motivated attacker with device access could extract.
21
+ * - The offline session token (`offlineToken`) is a random UUID that serves as
22
+ * a local-only proof of authentication. It is NOT a JWT and carries no claims.
23
+ * - `getOfflineLoginInfo` intentionally excludes the password hash and userId
24
+ * from its return value to minimize exposure of sensitive data.
25
+ *
26
+ * @module auth/offlineLogin
27
+ */
28
+ /**
29
+ * Structured result returned by {@link signInOffline}.
30
+ *
31
+ * On success: `{ success: true }`.
32
+ * On failure: `{ success: false, reason }` where `reason` identifies which
33
+ * step of the offline login flow failed.
6
34
  */
7
35
  interface OfflineLoginResult {
36
+ /** Whether the offline login succeeded. */
8
37
  success: boolean;
38
+ /** Human-readable error message (currently unused; `reason` is preferred). */
9
39
  error?: string;
40
+ /**
41
+ * Machine-readable failure reason, used by the login UI to display
42
+ * context-appropriate error messages.
43
+ *
44
+ * - `'no_credentials'` -- No cached credentials exist on this device.
45
+ * - `'no_stored_password'` -- Credentials exist but the password hash is missing.
46
+ * - `'user_mismatch'` -- The cached userId does not match the expected user.
47
+ * - `'email_mismatch'` -- The entered email does not match the cached email.
48
+ * - `'password_mismatch'` -- The entered password does not match the cached hash.
49
+ * - `'session_failed'` -- Credential verification passed but session creation
50
+ * or persistence failed.
51
+ */
10
52
  reason?: 'no_credentials' | 'no_stored_password' | 'user_mismatch' | 'email_mismatch' | 'password_mismatch' | 'session_failed';
11
53
  }
12
54
  /**
13
55
  * Sign in offline using cached credentials.
14
56
  *
15
- * 1. Fetches cached offline credentials
16
- * 2. Verifies email + password against cached credentials
17
- * 3. Creates offline session
18
- * 4. Validates session was persisted
19
- * 5. Returns structured result with typed error reasons
57
+ * Executes the full offline authentication flow:
58
+ * 1. Fetches cached offline credentials from IndexedDB.
59
+ * 2. Verifies the provided email + password against cached credentials.
60
+ * 3. Creates an offline session (random UUID token) in IndexedDB.
61
+ * 4. Validates the session was actually persisted (read-back check).
62
+ * 5. Returns a structured result with a typed error reason on failure.
63
+ *
64
+ * @param email - The email address entered by the user.
65
+ * @param password - The plaintext password entered by the user.
66
+ * @returns A promise resolving to an {@link OfflineLoginResult}.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const result = await signInOffline(email, password);
71
+ * if (result.success) {
72
+ * router.push('/dashboard');
73
+ * } else if (result.reason === 'no_credentials') {
74
+ * showError('You must log in online at least once before using offline mode.');
75
+ * } else {
76
+ * showError('Incorrect email or password.');
77
+ * }
78
+ * ```
79
+ *
80
+ * @see {@link getOfflineLoginInfo} to check if offline credentials are available.
81
+ * @see {@link verifyOfflineCredentials} for the underlying verification logic.
82
+ * @see {@link createOfflineSession} for session creation details.
20
83
  */
21
84
  export declare function signInOffline(email: string, password: string): Promise<OfflineLoginResult>;
22
85
  /**
23
86
  * Get non-sensitive display info about cached offline credentials.
24
- * Returns null if no credentials cached.
25
- * Used by login page to show "Sign in as X" offline UI.
87
+ *
88
+ * Used by the login page to show an "offline mode" UI with the cached
89
+ * user's name and email (e.g., "Sign in as jane@example.com").
90
+ *
91
+ * Returns `null` if no credentials are cached, meaning the user has never
92
+ * logged in online on this device and offline login is unavailable.
93
+ *
94
+ * @returns An object containing `hasCredentials`, `email`, `firstName`, and
95
+ * `lastName`, or `null` if no credentials exist.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const info = await getOfflineLoginInfo();
100
+ * if (info?.hasCredentials) {
101
+ * showOfflineLoginUI(info.email, info.firstName);
102
+ * } else {
103
+ * showOnlineOnlyMessage();
104
+ * }
105
+ * ```
106
+ *
107
+ * @see {@link signInOffline} to perform the actual offline login.
26
108
  */
27
109
  export declare function getOfflineLoginInfo(): Promise<{
110
+ /** Whether cached credentials exist on this device. */
28
111
  hasCredentials: boolean;
112
+ /** The cached user's email address. */
29
113
  email?: string;
114
+ /** The cached user's first name (from profile). */
30
115
  firstName?: string;
116
+ /** The cached user's last name (from profile). */
31
117
  lastName?: string;
32
118
  } | null>;
33
119
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"offlineLogin.d.ts","sourceRoot":"","sources":["../../src/auth/offlineLogin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,UAAU,kBAAkB;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EACH,gBAAgB,GAChB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,mBAAmB,GACnB,gBAAgB,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAoChG;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CAAC,CAiBR"}
1
+ {"version":3,"file":"offlineLogin.d.ts","sourceRoot":"","sources":["../../src/auth/offlineLogin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAUH;;;;;;GAMG;AACH,UAAU,kBAAkB;IAC1B,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;IAEjB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;;;;;;OAWG;IACH,MAAM,CAAC,EACH,gBAAgB,GAChB,oBAAoB,GACpB,eAAe,GACf,gBAAgB,GAChB,mBAAmB,GACnB,gBAAgB,CAAC;CACtB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsChG;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,uDAAuD;IACvD,cAAc,EAAE,OAAO,CAAC;IACxB,uCAAuC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CAAC,CAkBR"}