@aurabase/core 0.6.2 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -90,17 +90,21 @@ var AuraError = class _AuraError extends Error {
90
90
  this.code = canonical;
91
91
  } else if (status === 429) {
92
92
  this.code = "RATE_LIMITED";
93
- } else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
93
+ } else if (status === 401 && lowerMsg.includes("credentials")) {
94
94
  this.code = "INVALID_CREDENTIALS";
95
- } else if (status === 401 && (lowerMsg.includes("r\xE9voqu\xE9e") || lowerMsg.includes("revoked"))) {
95
+ } else if (status === 401 && lowerMsg.includes("revoked")) {
96
96
  this.code = "SESSION_REVOKED";
97
- } else if (status === 401 && (lowerMsg.includes("expir\xE9") || lowerMsg.includes("expired"))) {
97
+ } else if (status === 401 && lowerMsg.includes("expired")) {
98
98
  this.code = "SESSION_EXPIRED";
99
- } else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
99
+ } else if (status === 400 && (lowerMsg.includes("current password") || lowerMsg.includes("old password") || lowerMsg.includes("password") && lowerMsg.includes("required"))) {
100
+ this.code = lowerMsg.includes("incorrect") || lowerMsg.includes("invalid") ? "INVALID_CURRENT_PASSWORD" : "CURRENT_PASSWORD_REQUIRED";
101
+ } else if (status === 400 && lowerMsg.includes("password") && (lowerMsg.includes("weak") || lowerMsg.includes("length") || lowerMsg.includes("strength") || lowerMsg.includes("character") || lowerMsg.includes("digit") || lowerMsg.includes("pwned") || lowerMsg.includes("too short") || lowerMsg.includes("too common"))) {
100
102
  this.code = "WEAK_PASSWORD";
101
- } else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
103
+ } else if (status === 400 && lowerMsg.includes("password")) {
104
+ this.code = "INVALID_PASSWORD";
105
+ } else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
102
106
  this.code = "EMAIL_ALREADY_IN_USE";
103
- } else if (status === 404 && (lowerMsg.includes("utilisateur") || lowerMsg.includes("user not found"))) {
107
+ } else if (status === 404 && lowerMsg.includes("user not found")) {
104
108
  this.code = "USER_NOT_FOUND";
105
109
  } else {
106
110
  this.code = canonical ?? `HTTP_${status}`;
@@ -279,6 +283,33 @@ var AUTH_SETTINGS_WRITABLE_FIELDS = [
279
283
  "hook_secret"
280
284
  ];
281
285
 
286
+ // src/cookie-utils.ts
287
+ function parseCookieHeader(header) {
288
+ const result = {};
289
+ if (!header) return result;
290
+ for (const pair of header.split(";")) {
291
+ const idx = pair.indexOf("=");
292
+ if (idx === -1) continue;
293
+ const key = pair.slice(0, idx).trim();
294
+ const value = pair.slice(idx + 1).trim();
295
+ if (key) result[key] = decodeURIComponent(value);
296
+ }
297
+ return result;
298
+ }
299
+ function sameSiteAttribute(v) {
300
+ return v.charAt(0).toUpperCase() + v.slice(1);
301
+ }
302
+ function serializeCookieHeader(name, value, options = {}) {
303
+ const parts = [`${name}=${encodeURIComponent(value)}`];
304
+ if (options.path) parts.push(`Path=${options.path}`);
305
+ if (options.domain) parts.push(`Domain=${options.domain}`);
306
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
307
+ if (options.secure) parts.push("Secure");
308
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
309
+ if (options.httpOnly) parts.push("HttpOnly");
310
+ return parts.join("; ");
311
+ }
312
+
282
313
  // src/stores/BaseAuthStore.ts
283
314
  function decodeJwtPayload(token) {
284
315
  try {
@@ -292,6 +323,24 @@ function decodeJwtPayload(token) {
292
323
  return null;
293
324
  }
294
325
  }
326
+ function userFromAccessToken(token) {
327
+ const c = decodeJwtPayload(token);
328
+ if (!c || typeof c.sub !== "string") return null;
329
+ const meta = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
330
+ const amr = Array.isArray(c.amr) ? c.amr : [];
331
+ return {
332
+ id: c.sub,
333
+ email: typeof c.email === "string" ? c.email : null,
334
+ role: "authenticated",
335
+ email_verified: false,
336
+ is_banned: false,
337
+ is_anonymous: amr.some((a) => a?.method === "anonymous"),
338
+ mfa_enabled: c.aal === "aal2",
339
+ user_metadata: meta(c.user_metadata),
340
+ app_metadata: meta(c.app_metadata),
341
+ created_at: ""
342
+ };
343
+ }
295
344
  function isTokenExpired(token, marginSec = 0) {
296
345
  const payload = decodeJwtPayload(token);
297
346
  if (!payload || typeof payload.exp !== "number") return true;
@@ -305,6 +354,7 @@ var BaseAuthStore = class {
305
354
  * le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
306
355
  */
307
356
  persistent = false;
357
+ key;
308
358
  _token = null;
309
359
  _refreshToken = null;
310
360
  _user = null;
@@ -315,6 +365,9 @@ var BaseAuthStore = class {
315
365
  /** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
316
366
  _onEvent;
317
367
  _debug = false;
368
+ constructor(key = "aura-auth") {
369
+ this.key = key;
370
+ }
318
371
  // -- Getters ----------------------------------------------------------------
319
372
  /** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
320
373
  get removalEpoch() {
@@ -376,6 +429,43 @@ var BaseAuthStore = class {
376
429
  _emitStorageBlocked(op) {
377
430
  emitAuraEvent(this._onEvent, this._debug, { kind: "storage-blocked", op });
378
431
  }
432
+ /**
433
+ * Populate the store from a raw `Cookie` header string (server-side).
434
+ * Looks for the key matching `this.key`.
435
+ */
436
+ loadFromCookie(cookieString) {
437
+ const prefix = `${this.key}=`;
438
+ const entry = cookieString.split(";").map((c) => c.trim()).find((c) => c.startsWith(prefix));
439
+ if (!entry) return;
440
+ try {
441
+ const raw = decodeURIComponent(entry.slice(prefix.length));
442
+ const parsed = JSON.parse(raw);
443
+ const user = parsed.token ? userFromAccessToken(parsed.token) : null;
444
+ if (parsed.token && parsed.refreshToken && user) {
445
+ this.save(parsed.token, parsed.refreshToken, user);
446
+ }
447
+ } catch {
448
+ }
449
+ }
450
+ /**
451
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
452
+ * `user_metadata` is already inside the access token.
453
+ */
454
+ exportToCookie(options = {}) {
455
+ const data = {
456
+ token: this.token,
457
+ refreshToken: this.refreshToken
458
+ };
459
+ const encoded = encodeURIComponent(JSON.stringify(data));
460
+ const parts = [`${this.key}=${encoded}`];
461
+ if (options.path) parts.push(`Path=${options.path}`);
462
+ if (options.domain) parts.push(`Domain=${options.domain}`);
463
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
464
+ if (options.secure) parts.push("Secure");
465
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
466
+ if (options.httpOnly) parts.push("HttpOnly");
467
+ return parts.join("; ");
468
+ }
379
469
  // -- Event emitter ----------------------------------------------------------
380
470
  /**
381
471
  * Register a callback that fires whenever the auth state changes.
@@ -870,45 +960,16 @@ var BaseService = class {
870
960
  }
871
961
  };
872
962
 
873
- // src/cookie-utils.ts
874
- function parseCookieHeader(header) {
875
- const result = {};
876
- if (!header) return result;
877
- for (const pair of header.split(";")) {
878
- const idx = pair.indexOf("=");
879
- if (idx === -1) continue;
880
- const key = pair.slice(0, idx).trim();
881
- const value = pair.slice(idx + 1).trim();
882
- if (key) result[key] = decodeURIComponent(value);
883
- }
884
- return result;
885
- }
886
- function sameSiteAttribute(v) {
887
- return v.charAt(0).toUpperCase() + v.slice(1);
888
- }
889
- function serializeCookieHeader(name, value, options = {}) {
890
- const parts = [`${name}=${encodeURIComponent(value)}`];
891
- if (options.path) parts.push(`Path=${options.path}`);
892
- if (options.domain) parts.push(`Domain=${options.domain}`);
893
- if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
894
- if (options.secure) parts.push("Secure");
895
- if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
896
- if (options.httpOnly) parts.push("HttpOnly");
897
- return parts.join("; ");
898
- }
899
-
900
963
  // src/stores/LocalAuthStore.ts
901
964
  var LocalAuthStore = class extends BaseAuthStore {
902
965
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
903
966
  persistent = true;
904
- key;
905
967
  /**
906
968
  * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
907
969
  * `aura-${clientScope(baseUrl)}-auth`.
908
970
  */
909
971
  constructor(key) {
910
- super();
911
- this.key = key;
972
+ super(key);
912
973
  this._loadFromStorage();
913
974
  }
914
975
  // -- Overrides --------------------------------------------------------------
@@ -983,6 +1044,9 @@ var LocalAuthStore = class extends BaseAuthStore {
983
1044
  /**
984
1045
  * Populate the store from a raw `Cookie` header string (server-side).
985
1046
  * Looks for the key matching `this.key`.
1047
+ *
1048
+ * Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
1049
+ * claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
986
1050
  */
987
1051
  loadFromCookie(cookieString) {
988
1052
  const prefix = `${this.key}=`;
@@ -991,20 +1055,22 @@ var LocalAuthStore = class extends BaseAuthStore {
991
1055
  try {
992
1056
  const raw = decodeURIComponent(entry.slice(prefix.length));
993
1057
  const parsed = JSON.parse(raw);
994
- if (parsed.token && parsed.refreshToken && parsed.user) {
995
- super.save(parsed.token, parsed.refreshToken, parsed.user);
1058
+ const user = parsed.token ? userFromAccessToken(parsed.token) : null;
1059
+ if (parsed.token && parsed.refreshToken && user) {
1060
+ super.save(parsed.token, parsed.refreshToken, user);
996
1061
  }
997
1062
  } catch {
998
1063
  }
999
1064
  }
1000
1065
  /**
1001
- * Serialize current auth state into a `Set-Cookie` value.
1066
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
1067
+ * `user_metadata` is already inside the access token, and sending it twice doubled the cookie
1068
+ * (5 KB of metadata gave 4 cookies and 13 139 bytes).
1002
1069
  */
1003
1070
  exportToCookie(options = {}) {
1004
1071
  const data = {
1005
1072
  token: this.token,
1006
- refreshToken: this.refreshToken,
1007
- user: this.user
1073
+ refreshToken: this.refreshToken
1008
1074
  };
1009
1075
  const encoded = encodeURIComponent(JSON.stringify(data));
1010
1076
  const parts = [`${this.key}=${encoded}`];
package/dist/index.d.cts CHANGED
@@ -14,7 +14,7 @@
14
14
  * - storage/path.ts : invalid_path
15
15
  * - db/QueryBuilder.ts: PGRST116 (parité PostgREST : `.single()` sur 0/plusieurs lignes)
16
16
  */
17
- type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | (string & {});
17
+ type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'PASSWORD_REQUIRED' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'USER_BANNED' | 'EMAIL_NOT_CONFIRMED' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | 'MFA_CODE_INVALID' | 'MFA_CODE_ALREADY_USED' | 'TOTP_CODE_REUSED' | 'MFA_CHALLENGE_EXPIRED' | (string & {});
18
18
  /**
19
19
  * Structured error thrown/returned by the Aurabase SDK.
20
20
  * Mirrors the backend's `AuraError` JSON shape with semantic DX normalization.
@@ -34,6 +34,8 @@ declare class AuraError extends Error {
34
34
  error?: {
35
35
  code?: string;
36
36
  message?: string;
37
+ hint?: string;
38
+ details?: unknown;
37
39
  };
38
40
  message?: string;
39
41
  } | null, headers?: Headers | Record<string, string | null | undefined>);
@@ -201,12 +203,10 @@ interface User {
201
203
  last_sign_in_at?: string | null;
202
204
  }
203
205
  interface Session {
206
+ /** Identifiant unique de la session (UUID ou 'sess_...'), dérivé du claim `session_id` du JWT. */
207
+ id?: string;
204
208
  access_token: string;
205
209
  refresh_token: string;
206
- /** Alias camelCase pour parité DX Supabase/JS */
207
- accessToken?: string;
208
- /** Alias camelCase pour parité DX Supabase/JS */
209
- refreshToken?: string;
210
210
  token_type: string;
211
211
  expires_in: number;
212
212
  expires_at?: number;
@@ -217,30 +217,20 @@ interface Session {
217
217
  interface TokenPair {
218
218
  access_token: string;
219
219
  refresh_token: string;
220
- /** Alias camelCase pour parité DX Supabase/JS */
221
- accessToken?: string;
222
- /** Alias camelCase pour parité DX Supabase/JS */
223
- refreshToken?: string;
224
220
  token_type: string;
225
221
  expires_in: number;
226
222
  user: User;
227
223
  provider_token?: string | null;
228
224
  provider_refresh_token?: string | null;
229
- /** Sous-objet session pour parité DX Supabase (`data.session.access_token`). */
230
- session?: Session;
231
- mfaChallenge?: undefined;
225
+ /** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
226
+ * `null` après une inscription sur un projet qui exige un e-mail vérifié (AUTH-002). */
227
+ session?: Session | null;
232
228
  }
233
229
  interface MfaChallenge {
234
230
  mfa_required: true;
235
231
  mfa_token: string;
236
232
  mfa_setup_required?: boolean;
237
233
  session?: undefined;
238
- /** Alias ergonomique aligné sur la documentation officielle */
239
- mfaChallenge?: {
240
- mfaRequired: boolean;
241
- mfaToken: string;
242
- mfaSetupRequired?: boolean;
243
- };
244
234
  }
245
235
  type LoginResponse = TokenPair | MfaChallenge;
246
236
  type AuthEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'MFA_CHALLENGE' | 'PASSWORD_RECOVERY';
@@ -279,6 +269,7 @@ interface SignUpRequest {
279
269
  options?: {
280
270
  emailRedirectTo?: string;
281
271
  data?: Record<string, unknown>;
272
+ preventEmailEnumeration?: boolean;
282
273
  };
283
274
  }
284
275
  interface SignInRequest {
@@ -375,9 +366,46 @@ interface MfaEnrollResponse {
375
366
  otpauth_url: string;
376
367
  secret: string;
377
368
  }
378
- interface MfaVerifyRequest {
379
- mfa_token: string;
369
+ /**
370
+ * Relève un défi MFA : exactement UN facteur parmi `code` (TOTP à 6 chiffres), `recovery_code`
371
+ * (code de secours `XXXXXXXX-XXXXXXXX`) et `sms_code` (code reçu après `auth.mfa.sms.send`).
372
+ * Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
373
+ */
374
+ type MfaVerifyRequest = {
375
+ mfa_token?: string;
376
+ mfaToken?: string;
377
+ challenge_id?: string;
378
+ challengeId?: string;
380
379
  code: string;
380
+ } | {
381
+ mfa_token?: string;
382
+ mfaToken?: string;
383
+ challenge_id?: string;
384
+ challengeId?: string;
385
+ recovery_code: string;
386
+ recoveryCode?: string;
387
+ } | {
388
+ mfa_token?: string;
389
+ mfaToken?: string;
390
+ challenge_id?: string;
391
+ challengeId?: string;
392
+ sms_code: string;
393
+ smsCode?: string;
394
+ };
395
+ /** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
396
+ type MfaFactorType = 'totp' | 'webauthn' | 'sms';
397
+ /** Passkey WebAuthn enrôlée (`auth.mfa.webauthn.list`). */
398
+ interface PasskeyInfo {
399
+ id: string;
400
+ friendly_name: string;
401
+ created_at: string;
402
+ last_used_at: string | null;
403
+ }
404
+ /** Résultat de l'enrôlement d'une passkey. `recovery_codes` n'est présent que si cette passkey est le premier facteur du compte. */
405
+ interface PasskeyRegisterResult {
406
+ id: string;
407
+ friendly_name: string;
408
+ recovery_codes?: string[];
381
409
  }
382
410
  interface SessionInfo {
383
411
  id: string;
@@ -1504,6 +1532,11 @@ interface AuraClientOptions {
1504
1532
  * Défaut : `true` en navigateur, no-op hors navigateur (SSR/Node).
1505
1533
  */
1506
1534
  detectSessionInUrl?: boolean;
1535
+ /**
1536
+ * AUTH-015 — flux OAuth par défaut de `signInWithOAuth` pour ce client (`'pkce'` recommandé
1537
+ * en navigateur). Une option `flowType` passée à l'appel reste prioritaire.
1538
+ */
1539
+ flowType?: 'pkce' | 'implicit';
1507
1540
  };
1508
1541
  fetch?: typeof globalThis.fetch;
1509
1542
  /**
@@ -1787,7 +1820,7 @@ interface AdminUpdateUserRequest {
1787
1820
  mfa_enabled?: boolean;
1788
1821
  revoke_sessions?: boolean;
1789
1822
  }
1790
- type GenerateLinkType = 'magic' | 'recovery' | 'invite';
1823
+ type GenerateLinkType = 'signup' | 'magic' | 'recovery' | 'invite';
1791
1824
  interface GenerateLinkResult {
1792
1825
  link: string;
1793
1826
  token: string;
@@ -1820,6 +1853,19 @@ type AuthChangeCallback = (token: string | null, user: User | null) => void;
1820
1853
  * Check whether a JWT has expired (or will expire within `marginSec` seconds).
1821
1854
  */
1822
1855
  declare function isTokenExpired(token: string, marginSec?: number): boolean;
1856
+ interface CookieOptions {
1857
+ path?: string;
1858
+ domain?: string;
1859
+ maxAge?: number;
1860
+ secure?: boolean;
1861
+ sameSite?: 'strict' | 'lax' | 'none';
1862
+ httpOnly?: boolean;
1863
+ }
1864
+ interface StoredAuth {
1865
+ token: string | null;
1866
+ refreshToken: string | null;
1867
+ user: User | null;
1868
+ }
1823
1869
  /**
1824
1870
  * In-memory auth store with an event-emitter pattern.
1825
1871
  * Can be extended (see LocalAuthStore) for persistence.
@@ -1831,6 +1877,7 @@ declare class BaseAuthStore {
1831
1877
  * le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
1832
1878
  */
1833
1879
  readonly persistent: boolean;
1880
+ protected readonly key: string;
1834
1881
  private _token;
1835
1882
  private _refreshToken;
1836
1883
  private _user;
@@ -1841,6 +1888,7 @@ declare class BaseAuthStore {
1841
1888
  /** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
1842
1889
  private _onEvent?;
1843
1890
  private _debug;
1891
+ constructor(key?: string);
1844
1892
  /** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
1845
1893
  get removalEpoch(): number;
1846
1894
  get token(): string | null;
@@ -1862,6 +1910,16 @@ declare class BaseAuthStore {
1862
1910
  setObservabilityHook(onEvent: ((event: AuraObservabilityEvent) => void) | undefined, debug: boolean | undefined): void;
1863
1911
  /** Émet `{kind:'storage-blocked', op}` — ne throw jamais (voir `emitAuraEvent`). */
1864
1912
  protected _emitStorageBlocked(op: string): void;
1913
+ /**
1914
+ * Populate the store from a raw `Cookie` header string (server-side).
1915
+ * Looks for the key matching `this.key`.
1916
+ */
1917
+ loadFromCookie(cookieString: string): void;
1918
+ /**
1919
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
1920
+ * `user_metadata` is already inside the access token.
1921
+ */
1922
+ exportToCookie(options?: CookieOptions): string;
1865
1923
  /**
1866
1924
  * Register a callback that fires whenever the auth state changes.
1867
1925
  * Returns an unsubscribe function.
@@ -2019,14 +2077,6 @@ declare function clientScope(baseUrl: string): string;
2019
2077
  /** JSON.stringify avec support des bigint (sérialisés en string). */
2020
2078
  declare function serializeBody(body: unknown): string;
2021
2079
 
2022
- interface CookieOptions {
2023
- path?: string;
2024
- domain?: string;
2025
- maxAge?: number;
2026
- secure?: boolean;
2027
- sameSite?: 'strict' | 'lax' | 'none';
2028
- httpOnly?: boolean;
2029
- }
2030
2080
  /**
2031
2081
  * Auth store backed by `localStorage` (browser) with cookie helpers for SSR.
2032
2082
  *
@@ -2043,7 +2093,6 @@ interface CookieOptions {
2043
2093
  declare class LocalAuthStore extends BaseAuthStore {
2044
2094
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
2045
2095
  readonly persistent = true;
2046
- private readonly key;
2047
2096
  /**
2048
2097
  * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
2049
2098
  * `aura-${clientScope(baseUrl)}-auth`.
@@ -2067,10 +2116,15 @@ declare class LocalAuthStore extends BaseAuthStore {
2067
2116
  /**
2068
2117
  * Populate the store from a raw `Cookie` header string (server-side).
2069
2118
  * Looks for the key matching `this.key`.
2119
+ *
2120
+ * Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
2121
+ * claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
2070
2122
  */
2071
2123
  loadFromCookie(cookieString: string): void;
2072
2124
  /**
2073
- * Serialize current auth state into a `Set-Cookie` value.
2125
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
2126
+ * `user_metadata` is already inside the access token, and sending it twice doubled the cookie
2127
+ * (5 KB of metadata gave 4 cookies and 13 139 bytes).
2074
2128
  */
2075
2129
  exportToCookie(options?: CookieOptions): string;
2076
2130
  private _getStorage;
@@ -2079,4 +2133,4 @@ declare class LocalAuthStore extends BaseAuthStore {
2079
2133
  private _removeStorage;
2080
2134
  }
2081
2135
 
2082
- export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
2136
+ export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CookieOptions, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type StoredAuth, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
package/dist/index.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * - storage/path.ts : invalid_path
15
15
  * - db/QueryBuilder.ts: PGRST116 (parité PostgREST : `.single()` sur 0/plusieurs lignes)
16
16
  */
17
- type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | (string & {});
17
+ type AuraErrorCode = 'NO_SESSION' | 'TIMEOUT' | 'NETWORK_ERROR' | 'INVALID_RESPONSE' | 'REFRESH_DISCARDED' | 'UNVERIFIABLE_TOKEN' | 'NO_CODE_VERIFIER' | 'PKCE_GENERATION_FAILED' | 'MISSING_EMAIL' | 'JWKS_FETCH_FAILED' | 'SIGNATURE_INVALID' | 'TOKEN_EXPIRED' | 'TOKEN_NOT_YET_VALID' | 'INVALID_JWT' | 'UNSUPPORTED_ALG' | 'NO_MATCHING_KEY' | 'INVALID_ISSUER' | 'INVALID_AUDIENCE' | 'INVALID_PATH' | 'PGRST116' | 'INVALID_CREDENTIALS' | 'WEAK_PASSWORD' | 'PASSWORD_REQUIRED' | 'EMAIL_ALREADY_IN_USE' | 'USER_NOT_FOUND' | 'USER_BANNED' | 'EMAIL_NOT_CONFIRMED' | 'RATE_LIMITED' | 'SESSION_EXPIRED' | 'INVALID_OTP' | 'SESSION_REVOKED' | 'MFA_CODE_INVALID' | 'MFA_CODE_ALREADY_USED' | 'TOTP_CODE_REUSED' | 'MFA_CHALLENGE_EXPIRED' | (string & {});
18
18
  /**
19
19
  * Structured error thrown/returned by the Aurabase SDK.
20
20
  * Mirrors the backend's `AuraError` JSON shape with semantic DX normalization.
@@ -34,6 +34,8 @@ declare class AuraError extends Error {
34
34
  error?: {
35
35
  code?: string;
36
36
  message?: string;
37
+ hint?: string;
38
+ details?: unknown;
37
39
  };
38
40
  message?: string;
39
41
  } | null, headers?: Headers | Record<string, string | null | undefined>);
@@ -201,12 +203,10 @@ interface User {
201
203
  last_sign_in_at?: string | null;
202
204
  }
203
205
  interface Session {
206
+ /** Identifiant unique de la session (UUID ou 'sess_...'), dérivé du claim `session_id` du JWT. */
207
+ id?: string;
204
208
  access_token: string;
205
209
  refresh_token: string;
206
- /** Alias camelCase pour parité DX Supabase/JS */
207
- accessToken?: string;
208
- /** Alias camelCase pour parité DX Supabase/JS */
209
- refreshToken?: string;
210
210
  token_type: string;
211
211
  expires_in: number;
212
212
  expires_at?: number;
@@ -217,30 +217,20 @@ interface Session {
217
217
  interface TokenPair {
218
218
  access_token: string;
219
219
  refresh_token: string;
220
- /** Alias camelCase pour parité DX Supabase/JS */
221
- accessToken?: string;
222
- /** Alias camelCase pour parité DX Supabase/JS */
223
- refreshToken?: string;
224
220
  token_type: string;
225
221
  expires_in: number;
226
222
  user: User;
227
223
  provider_token?: string | null;
228
224
  provider_refresh_token?: string | null;
229
- /** Sous-objet session pour parité DX Supabase (`data.session.access_token`). */
230
- session?: Session;
231
- mfaChallenge?: undefined;
225
+ /** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
226
+ * `null` après une inscription sur un projet qui exige un e-mail vérifié (AUTH-002). */
227
+ session?: Session | null;
232
228
  }
233
229
  interface MfaChallenge {
234
230
  mfa_required: true;
235
231
  mfa_token: string;
236
232
  mfa_setup_required?: boolean;
237
233
  session?: undefined;
238
- /** Alias ergonomique aligné sur la documentation officielle */
239
- mfaChallenge?: {
240
- mfaRequired: boolean;
241
- mfaToken: string;
242
- mfaSetupRequired?: boolean;
243
- };
244
234
  }
245
235
  type LoginResponse = TokenPair | MfaChallenge;
246
236
  type AuthEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'MFA_CHALLENGE' | 'PASSWORD_RECOVERY';
@@ -279,6 +269,7 @@ interface SignUpRequest {
279
269
  options?: {
280
270
  emailRedirectTo?: string;
281
271
  data?: Record<string, unknown>;
272
+ preventEmailEnumeration?: boolean;
282
273
  };
283
274
  }
284
275
  interface SignInRequest {
@@ -375,9 +366,46 @@ interface MfaEnrollResponse {
375
366
  otpauth_url: string;
376
367
  secret: string;
377
368
  }
378
- interface MfaVerifyRequest {
379
- mfa_token: string;
369
+ /**
370
+ * Relève un défi MFA : exactement UN facteur parmi `code` (TOTP à 6 chiffres), `recovery_code`
371
+ * (code de secours `XXXXXXXX-XXXXXXXX`) et `sms_code` (code reçu après `auth.mfa.sms.send`).
372
+ * Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
373
+ */
374
+ type MfaVerifyRequest = {
375
+ mfa_token?: string;
376
+ mfaToken?: string;
377
+ challenge_id?: string;
378
+ challengeId?: string;
380
379
  code: string;
380
+ } | {
381
+ mfa_token?: string;
382
+ mfaToken?: string;
383
+ challenge_id?: string;
384
+ challengeId?: string;
385
+ recovery_code: string;
386
+ recoveryCode?: string;
387
+ } | {
388
+ mfa_token?: string;
389
+ mfaToken?: string;
390
+ challenge_id?: string;
391
+ challengeId?: string;
392
+ sms_code: string;
393
+ smsCode?: string;
394
+ };
395
+ /** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
396
+ type MfaFactorType = 'totp' | 'webauthn' | 'sms';
397
+ /** Passkey WebAuthn enrôlée (`auth.mfa.webauthn.list`). */
398
+ interface PasskeyInfo {
399
+ id: string;
400
+ friendly_name: string;
401
+ created_at: string;
402
+ last_used_at: string | null;
403
+ }
404
+ /** Résultat de l'enrôlement d'une passkey. `recovery_codes` n'est présent que si cette passkey est le premier facteur du compte. */
405
+ interface PasskeyRegisterResult {
406
+ id: string;
407
+ friendly_name: string;
408
+ recovery_codes?: string[];
381
409
  }
382
410
  interface SessionInfo {
383
411
  id: string;
@@ -1504,6 +1532,11 @@ interface AuraClientOptions {
1504
1532
  * Défaut : `true` en navigateur, no-op hors navigateur (SSR/Node).
1505
1533
  */
1506
1534
  detectSessionInUrl?: boolean;
1535
+ /**
1536
+ * AUTH-015 — flux OAuth par défaut de `signInWithOAuth` pour ce client (`'pkce'` recommandé
1537
+ * en navigateur). Une option `flowType` passée à l'appel reste prioritaire.
1538
+ */
1539
+ flowType?: 'pkce' | 'implicit';
1507
1540
  };
1508
1541
  fetch?: typeof globalThis.fetch;
1509
1542
  /**
@@ -1787,7 +1820,7 @@ interface AdminUpdateUserRequest {
1787
1820
  mfa_enabled?: boolean;
1788
1821
  revoke_sessions?: boolean;
1789
1822
  }
1790
- type GenerateLinkType = 'magic' | 'recovery' | 'invite';
1823
+ type GenerateLinkType = 'signup' | 'magic' | 'recovery' | 'invite';
1791
1824
  interface GenerateLinkResult {
1792
1825
  link: string;
1793
1826
  token: string;
@@ -1820,6 +1853,19 @@ type AuthChangeCallback = (token: string | null, user: User | null) => void;
1820
1853
  * Check whether a JWT has expired (or will expire within `marginSec` seconds).
1821
1854
  */
1822
1855
  declare function isTokenExpired(token: string, marginSec?: number): boolean;
1856
+ interface CookieOptions {
1857
+ path?: string;
1858
+ domain?: string;
1859
+ maxAge?: number;
1860
+ secure?: boolean;
1861
+ sameSite?: 'strict' | 'lax' | 'none';
1862
+ httpOnly?: boolean;
1863
+ }
1864
+ interface StoredAuth {
1865
+ token: string | null;
1866
+ refreshToken: string | null;
1867
+ user: User | null;
1868
+ }
1823
1869
  /**
1824
1870
  * In-memory auth store with an event-emitter pattern.
1825
1871
  * Can be extended (see LocalAuthStore) for persistence.
@@ -1831,6 +1877,7 @@ declare class BaseAuthStore {
1831
1877
  * le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
1832
1878
  */
1833
1879
  readonly persistent: boolean;
1880
+ protected readonly key: string;
1834
1881
  private _token;
1835
1882
  private _refreshToken;
1836
1883
  private _user;
@@ -1841,6 +1888,7 @@ declare class BaseAuthStore {
1841
1888
  /** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
1842
1889
  private _onEvent?;
1843
1890
  private _debug;
1891
+ constructor(key?: string);
1844
1892
  /** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
1845
1893
  get removalEpoch(): number;
1846
1894
  get token(): string | null;
@@ -1862,6 +1910,16 @@ declare class BaseAuthStore {
1862
1910
  setObservabilityHook(onEvent: ((event: AuraObservabilityEvent) => void) | undefined, debug: boolean | undefined): void;
1863
1911
  /** Émet `{kind:'storage-blocked', op}` — ne throw jamais (voir `emitAuraEvent`). */
1864
1912
  protected _emitStorageBlocked(op: string): void;
1913
+ /**
1914
+ * Populate the store from a raw `Cookie` header string (server-side).
1915
+ * Looks for the key matching `this.key`.
1916
+ */
1917
+ loadFromCookie(cookieString: string): void;
1918
+ /**
1919
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
1920
+ * `user_metadata` is already inside the access token.
1921
+ */
1922
+ exportToCookie(options?: CookieOptions): string;
1865
1923
  /**
1866
1924
  * Register a callback that fires whenever the auth state changes.
1867
1925
  * Returns an unsubscribe function.
@@ -2019,14 +2077,6 @@ declare function clientScope(baseUrl: string): string;
2019
2077
  /** JSON.stringify avec support des bigint (sérialisés en string). */
2020
2078
  declare function serializeBody(body: unknown): string;
2021
2079
 
2022
- interface CookieOptions {
2023
- path?: string;
2024
- domain?: string;
2025
- maxAge?: number;
2026
- secure?: boolean;
2027
- sameSite?: 'strict' | 'lax' | 'none';
2028
- httpOnly?: boolean;
2029
- }
2030
2080
  /**
2031
2081
  * Auth store backed by `localStorage` (browser) with cookie helpers for SSR.
2032
2082
  *
@@ -2043,7 +2093,6 @@ interface CookieOptions {
2043
2093
  declare class LocalAuthStore extends BaseAuthStore {
2044
2094
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
2045
2095
  readonly persistent = true;
2046
- private readonly key;
2047
2096
  /**
2048
2097
  * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
2049
2098
  * `aura-${clientScope(baseUrl)}-auth`.
@@ -2067,10 +2116,15 @@ declare class LocalAuthStore extends BaseAuthStore {
2067
2116
  /**
2068
2117
  * Populate the store from a raw `Cookie` header string (server-side).
2069
2118
  * Looks for the key matching `this.key`.
2119
+ *
2120
+ * Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
2121
+ * claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
2070
2122
  */
2071
2123
  loadFromCookie(cookieString: string): void;
2072
2124
  /**
2073
- * Serialize current auth state into a `Set-Cookie` value.
2125
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
2126
+ * `user_metadata` is already inside the access token, and sending it twice doubled the cookie
2127
+ * (5 KB of metadata gave 4 cookies and 13 139 bytes).
2074
2128
  */
2075
2129
  exportToCookie(options?: CookieOptions): string;
2076
2130
  private _getStorage;
@@ -2079,4 +2133,4 @@ declare class LocalAuthStore extends BaseAuthStore {
2079
2133
  private _removeStorage;
2080
2134
  }
2081
2135
 
2082
- export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
2136
+ export { AUTH_SETTINGS_WRITABLE_FIELDS, type AdminCreateUserRequest, type AdminSessionInfo, type AdminUpdateUserRequest, type AdminUser, type ApnsPublicSettings, type ApnsSettingsPatch, type AuditLogEntry, type AuraClientLike, type AuraClientOptions, AuraError, type AuraErrorCode, type AuraFailure, type AuraNoContent, type AuraObservabilityEvent, type AuraResponse, type AuraSuccess, type AuthChangePayload, type AuthEvent, type AuthSettings, type AuthSettingsUpdate, BaseAuthStore, BaseService, type BatchSendItemResult, type BatchSendResponse, type BroadcastAckMessage, type BroadcastFilter, type BroadcastPayload, type BroadcastSendStatus, type CdcPayload, type ChannelConfig, type ChatMessage, type ChatResponse, type CookieOptions, type CountMode, type CreateNotificationRuleRequest, type CreatePgFunctionRequest, type CreatePgTriggerRequest, type CronJob, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT_MS, type EmailTemplateUpdate, type EmbedRequest, type EmbedResponse, type FcmPublicSettings, type FcmSettingsPatch, type FetchInit, type FileEntry, type FilterOp, type FunctionDef, type FunctionPermissions, type GenerateLinkResult, type GenerateLinkType, type GetClaimsResult, type IdentityInfo, type ImageTransform, type Job, type JwtClaims, LocalAuthStore, type LoginResponse, type MagicLinkRequest, type Meta, type MfaChallenge, type MfaEnrollResponse, type MfaFactorType, type MfaVerifyRequest, type Notification, type NotificationAttempt, type NotificationAttemptStatus, type NotificationChannel, type NotificationChannelStats, type NotificationDeadLetter, type NotificationEvent, type NotificationProviderLog, type NotificationQueueStats, type NotificationRule, type NotificationSettings, type NotificationSettingsPatch, type NotificationSettingsPublic, type NotificationStats, type NotificationStatsWindow, type NotificationStatus, type NotificationTemplate, type NotificationTemplatePreview, type OAuthProviderSetting, type OAuthSignInObjectOptions, type OAuthSignInOptions, type ObjectMeta, type PasskeyInfo, type PasskeyRegisterResult, type PasswordResetRequest, type PgExecutableRole, type PgFunctionInfo, type PgTriggerInfo, type PostgresChangesFilter, type PresenceFilter, type PresenceInfo, type PresenceState, type QueryFilter, type QueryOptions, type QuotaPublicSettings, type QuotaSettingsPatch, RETRYABLE_METHODS, RETRYABLE_STATUS, type RagIngestRequest, type RagIngestResponse, type RagRequest, type RagResponse, type RealtimeBroadcastEvent, type RealtimeEvent, type RealtimeListenType, type RealtimePostgresChangesBasePayload, type RealtimePostgresChangesPayload, type RealtimePostgresDeletePayload, type RealtimePostgresInsertPayload, type RealtimePostgresUpdatePayload, type RealtimeSubscribeStatus, type SchemaColumn, type SchemaEnum, type SchemaFunction, type SchemaFunctionArg, type SchemaIndex, type SchemaTable, type SemanticSearchResult, type SendNotificationItem, type SendNotificationOptions, type SendNotificationRequest, type SerializeCookieOptions, type ServerChannelRecoveredMsg, type ServerErrorMsg, type ServerEventMsg, type ServerMessage, type ServerPongMsg, type ServerPresenceStateMsg, type ServerSubscribedMsg, type ServerTokenRefreshedMsg, type Session, type SessionInfo, type SettingsSource, type SignInRequest, type SignUpRequest, type SmtpPublicSettings, type SmtpSettingsPatch, type StorageBucket, type StoredAuth, type SubscribeCallback, type TokenPair, type TwilioPublicSettings, type TwilioSettingsPatch, type UpdateNotificationRuleRequest, type UpdateUserRequest, type UploadResult, type User, type UserStats, type WebhookPublicSettings, type WebhookSettingsPatch, abortableSleep, clientScope, computeBackoff, emitAuraEvent, fetchWithAuth, isAbortError, isAuraError, isRetryableMethod, isRetryableStatus, isTokenExpired, parseCookieHeader, parseRetryAfter, refreshOnce, serializeBody, serializeCookieHeader };
package/dist/index.js CHANGED
@@ -88,17 +88,21 @@ var AuraError = class _AuraError extends Error {
88
88
  this.code = canonical;
89
89
  } else if (status === 429) {
90
90
  this.code = "RATE_LIMITED";
91
- } else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
91
+ } else if (status === 401 && lowerMsg.includes("credentials")) {
92
92
  this.code = "INVALID_CREDENTIALS";
93
- } else if (status === 401 && (lowerMsg.includes("r\xE9voqu\xE9e") || lowerMsg.includes("revoked"))) {
93
+ } else if (status === 401 && lowerMsg.includes("revoked")) {
94
94
  this.code = "SESSION_REVOKED";
95
- } else if (status === 401 && (lowerMsg.includes("expir\xE9") || lowerMsg.includes("expired"))) {
95
+ } else if (status === 401 && lowerMsg.includes("expired")) {
96
96
  this.code = "SESSION_EXPIRED";
97
- } else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
97
+ } else if (status === 400 && (lowerMsg.includes("current password") || lowerMsg.includes("old password") || lowerMsg.includes("password") && lowerMsg.includes("required"))) {
98
+ this.code = lowerMsg.includes("incorrect") || lowerMsg.includes("invalid") ? "INVALID_CURRENT_PASSWORD" : "CURRENT_PASSWORD_REQUIRED";
99
+ } else if (status === 400 && lowerMsg.includes("password") && (lowerMsg.includes("weak") || lowerMsg.includes("length") || lowerMsg.includes("strength") || lowerMsg.includes("character") || lowerMsg.includes("digit") || lowerMsg.includes("pwned") || lowerMsg.includes("too short") || lowerMsg.includes("too common"))) {
98
100
  this.code = "WEAK_PASSWORD";
99
- } else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
101
+ } else if (status === 400 && lowerMsg.includes("password")) {
102
+ this.code = "INVALID_PASSWORD";
103
+ } else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
100
104
  this.code = "EMAIL_ALREADY_IN_USE";
101
- } else if (status === 404 && (lowerMsg.includes("utilisateur") || lowerMsg.includes("user not found"))) {
105
+ } else if (status === 404 && lowerMsg.includes("user not found")) {
102
106
  this.code = "USER_NOT_FOUND";
103
107
  } else {
104
108
  this.code = canonical ?? `HTTP_${status}`;
@@ -277,6 +281,33 @@ var AUTH_SETTINGS_WRITABLE_FIELDS = [
277
281
  "hook_secret"
278
282
  ];
279
283
 
284
+ // src/cookie-utils.ts
285
+ function parseCookieHeader(header) {
286
+ const result = {};
287
+ if (!header) return result;
288
+ for (const pair of header.split(";")) {
289
+ const idx = pair.indexOf("=");
290
+ if (idx === -1) continue;
291
+ const key = pair.slice(0, idx).trim();
292
+ const value = pair.slice(idx + 1).trim();
293
+ if (key) result[key] = decodeURIComponent(value);
294
+ }
295
+ return result;
296
+ }
297
+ function sameSiteAttribute(v) {
298
+ return v.charAt(0).toUpperCase() + v.slice(1);
299
+ }
300
+ function serializeCookieHeader(name, value, options = {}) {
301
+ const parts = [`${name}=${encodeURIComponent(value)}`];
302
+ if (options.path) parts.push(`Path=${options.path}`);
303
+ if (options.domain) parts.push(`Domain=${options.domain}`);
304
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
305
+ if (options.secure) parts.push("Secure");
306
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
307
+ if (options.httpOnly) parts.push("HttpOnly");
308
+ return parts.join("; ");
309
+ }
310
+
280
311
  // src/stores/BaseAuthStore.ts
281
312
  function decodeJwtPayload(token) {
282
313
  try {
@@ -290,6 +321,24 @@ function decodeJwtPayload(token) {
290
321
  return null;
291
322
  }
292
323
  }
324
+ function userFromAccessToken(token) {
325
+ const c = decodeJwtPayload(token);
326
+ if (!c || typeof c.sub !== "string") return null;
327
+ const meta = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
328
+ const amr = Array.isArray(c.amr) ? c.amr : [];
329
+ return {
330
+ id: c.sub,
331
+ email: typeof c.email === "string" ? c.email : null,
332
+ role: "authenticated",
333
+ email_verified: false,
334
+ is_banned: false,
335
+ is_anonymous: amr.some((a) => a?.method === "anonymous"),
336
+ mfa_enabled: c.aal === "aal2",
337
+ user_metadata: meta(c.user_metadata),
338
+ app_metadata: meta(c.app_metadata),
339
+ created_at: ""
340
+ };
341
+ }
293
342
  function isTokenExpired(token, marginSec = 0) {
294
343
  const payload = decodeJwtPayload(token);
295
344
  if (!payload || typeof payload.exp !== "number") return true;
@@ -303,6 +352,7 @@ var BaseAuthStore = class {
303
352
  * le flux PKCE : le `code_verifier` doit survivre au redirect OAuth.
304
353
  */
305
354
  persistent = false;
355
+ key;
306
356
  _token = null;
307
357
  _refreshToken = null;
308
358
  _user = null;
@@ -313,6 +363,9 @@ var BaseAuthStore = class {
313
363
  /** Hook d'observabilité (LOT 3) — câblé par `AuraClient` via `setObservabilityHook`. */
314
364
  _onEvent;
315
365
  _debug = false;
366
+ constructor(key = "aura-auth") {
367
+ this.key = key;
368
+ }
316
369
  // -- Getters ----------------------------------------------------------------
317
370
  /** Compteur incrémenté à chaque `clear()` — sert au commit-guard anti-race du refresh. */
318
371
  get removalEpoch() {
@@ -374,6 +427,43 @@ var BaseAuthStore = class {
374
427
  _emitStorageBlocked(op) {
375
428
  emitAuraEvent(this._onEvent, this._debug, { kind: "storage-blocked", op });
376
429
  }
430
+ /**
431
+ * Populate the store from a raw `Cookie` header string (server-side).
432
+ * Looks for the key matching `this.key`.
433
+ */
434
+ loadFromCookie(cookieString) {
435
+ const prefix = `${this.key}=`;
436
+ const entry = cookieString.split(";").map((c) => c.trim()).find((c) => c.startsWith(prefix));
437
+ if (!entry) return;
438
+ try {
439
+ const raw = decodeURIComponent(entry.slice(prefix.length));
440
+ const parsed = JSON.parse(raw);
441
+ const user = parsed.token ? userFromAccessToken(parsed.token) : null;
442
+ if (parsed.token && parsed.refreshToken && user) {
443
+ this.save(parsed.token, parsed.refreshToken, user);
444
+ }
445
+ } catch {
446
+ }
447
+ }
448
+ /**
449
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
450
+ * `user_metadata` is already inside the access token.
451
+ */
452
+ exportToCookie(options = {}) {
453
+ const data = {
454
+ token: this.token,
455
+ refreshToken: this.refreshToken
456
+ };
457
+ const encoded = encodeURIComponent(JSON.stringify(data));
458
+ const parts = [`${this.key}=${encoded}`];
459
+ if (options.path) parts.push(`Path=${options.path}`);
460
+ if (options.domain) parts.push(`Domain=${options.domain}`);
461
+ if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
462
+ if (options.secure) parts.push("Secure");
463
+ if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
464
+ if (options.httpOnly) parts.push("HttpOnly");
465
+ return parts.join("; ");
466
+ }
377
467
  // -- Event emitter ----------------------------------------------------------
378
468
  /**
379
469
  * Register a callback that fires whenever the auth state changes.
@@ -868,45 +958,16 @@ var BaseService = class {
868
958
  }
869
959
  };
870
960
 
871
- // src/cookie-utils.ts
872
- function parseCookieHeader(header) {
873
- const result = {};
874
- if (!header) return result;
875
- for (const pair of header.split(";")) {
876
- const idx = pair.indexOf("=");
877
- if (idx === -1) continue;
878
- const key = pair.slice(0, idx).trim();
879
- const value = pair.slice(idx + 1).trim();
880
- if (key) result[key] = decodeURIComponent(value);
881
- }
882
- return result;
883
- }
884
- function sameSiteAttribute(v) {
885
- return v.charAt(0).toUpperCase() + v.slice(1);
886
- }
887
- function serializeCookieHeader(name, value, options = {}) {
888
- const parts = [`${name}=${encodeURIComponent(value)}`];
889
- if (options.path) parts.push(`Path=${options.path}`);
890
- if (options.domain) parts.push(`Domain=${options.domain}`);
891
- if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
892
- if (options.secure) parts.push("Secure");
893
- if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
894
- if (options.httpOnly) parts.push("HttpOnly");
895
- return parts.join("; ");
896
- }
897
-
898
961
  // src/stores/LocalAuthStore.ts
899
962
  var LocalAuthStore = class extends BaseAuthStore {
900
963
  /** Persiste en localStorage → survit au redirect pleine page (cf. PKCE). */
901
964
  persistent = true;
902
- key;
903
965
  /**
904
966
  * @param key Nom de l'entrée `localStorage` (et du cookie SSR). Scopez-le par projet :
905
967
  * `aura-${clientScope(baseUrl)}-auth`.
906
968
  */
907
969
  constructor(key) {
908
- super();
909
- this.key = key;
970
+ super(key);
910
971
  this._loadFromStorage();
911
972
  }
912
973
  // -- Overrides --------------------------------------------------------------
@@ -981,6 +1042,9 @@ var LocalAuthStore = class extends BaseAuthStore {
981
1042
  /**
982
1043
  * Populate the store from a raw `Cookie` header string (server-side).
983
1044
  * Looks for the key matching `this.key`.
1045
+ *
1046
+ * Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
1047
+ * claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
984
1048
  */
985
1049
  loadFromCookie(cookieString) {
986
1050
  const prefix = `${this.key}=`;
@@ -989,20 +1053,22 @@ var LocalAuthStore = class extends BaseAuthStore {
989
1053
  try {
990
1054
  const raw = decodeURIComponent(entry.slice(prefix.length));
991
1055
  const parsed = JSON.parse(raw);
992
- if (parsed.token && parsed.refreshToken && parsed.user) {
993
- super.save(parsed.token, parsed.refreshToken, parsed.user);
1056
+ const user = parsed.token ? userFromAccessToken(parsed.token) : null;
1057
+ if (parsed.token && parsed.refreshToken && user) {
1058
+ super.save(parsed.token, parsed.refreshToken, user);
994
1059
  }
995
1060
  } catch {
996
1061
  }
997
1062
  }
998
1063
  /**
999
- * Serialize current auth state into a `Set-Cookie` value.
1064
+ * Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
1065
+ * `user_metadata` is already inside the access token, and sending it twice doubled the cookie
1066
+ * (5 KB of metadata gave 4 cookies and 13 139 bytes).
1000
1067
  */
1001
1068
  exportToCookie(options = {}) {
1002
1069
  const data = {
1003
1070
  token: this.token,
1004
- refreshToken: this.refreshToken,
1005
- user: this.user
1071
+ refreshToken: this.refreshToken
1006
1072
  };
1007
1073
  const encoded = encodeURIComponent(JSON.stringify(data));
1008
1074
  const parts = [`${this.key}=${encoded}`];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurabase/core",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "description": "Cœur du SDK Aurabase : fetch résilient, erreurs, stores auth, types de réponse (infrastructure interne)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",