@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 +107 -41
- package/dist/index.d.cts +86 -32
- package/dist/index.d.ts +86 -32
- package/dist/index.js +107 -41
- package/package.json +1 -1
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 &&
|
|
93
|
+
} else if (status === 401 && lowerMsg.includes("credentials")) {
|
|
94
94
|
this.code = "INVALID_CREDENTIALS";
|
|
95
|
-
} else if (status === 401 &&
|
|
95
|
+
} else if (status === 401 && lowerMsg.includes("revoked")) {
|
|
96
96
|
this.code = "SESSION_REVOKED";
|
|
97
|
-
} else if (status === 401 &&
|
|
97
|
+
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
98
98
|
this.code = "SESSION_EXPIRED";
|
|
99
|
-
} else if (status === 400 && (lowerMsg.includes("
|
|
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 ===
|
|
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 &&
|
|
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
|
-
|
|
995
|
-
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
|
|
379
|
-
|
|
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
|
|
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 &&
|
|
91
|
+
} else if (status === 401 && lowerMsg.includes("credentials")) {
|
|
92
92
|
this.code = "INVALID_CREDENTIALS";
|
|
93
|
-
} else if (status === 401 &&
|
|
93
|
+
} else if (status === 401 && lowerMsg.includes("revoked")) {
|
|
94
94
|
this.code = "SESSION_REVOKED";
|
|
95
|
-
} else if (status === 401 &&
|
|
95
|
+
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
96
96
|
this.code = "SESSION_EXPIRED";
|
|
97
|
-
} else if (status === 400 && (lowerMsg.includes("
|
|
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 ===
|
|
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 &&
|
|
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
|
-
|
|
993
|
-
|
|
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
|
|
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