@aurabase/core 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +131 -47
- package/dist/index.d.cts +63 -25
- package/dist/index.d.ts +63 -25
- package/dist/index.js +131 -47
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -149,7 +149,7 @@ function hydrateFromCookieHeader(cookieHeader: string) {
|
|
|
149
149
|
const store = hydrateFromCookieHeader(`${CLE}=%7B%22token%22%3Anull%7D`)
|
|
150
150
|
|
|
151
151
|
// Après une mutation de session, sérialiser l'état courant en Set-Cookie.
|
|
152
|
-
const setCookieValue = store.exportToCookie({ path: '/', secure: true, sameSite: '
|
|
152
|
+
const setCookieValue = store.exportToCookie({ path: '/', secure: true, sameSite: 'lax', httpOnly: true })
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
`store.persistent` indique si le store survit à un redirect pleine page (`false` pour `BaseAuthStore`, `true` pour `LocalAuthStore`) — c'est ce flag que le flux OAuth PKCE utilise pour décider s'il peut stocker un `code_verifier` de façon fiable.
|
package/dist/index.cjs
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/AuraError.ts
|
|
4
|
+
var LEGACY_LOWERCASE_CODES = /* @__PURE__ */ new Set([
|
|
5
|
+
"NO_SESSION",
|
|
6
|
+
"TIMEOUT",
|
|
7
|
+
"NETWORK_ERROR",
|
|
8
|
+
"INVALID_RESPONSE",
|
|
9
|
+
"REFRESH_DISCARDED",
|
|
10
|
+
"UNVERIFIABLE_TOKEN",
|
|
11
|
+
"NO_CODE_VERIFIER",
|
|
12
|
+
"PKCE_GENERATION_FAILED",
|
|
13
|
+
"MISSING_EMAIL",
|
|
14
|
+
"JWKS_FETCH_FAILED",
|
|
15
|
+
"SIGNATURE_INVALID",
|
|
16
|
+
"TOKEN_EXPIRED",
|
|
17
|
+
"TOKEN_NOT_YET_VALID",
|
|
18
|
+
"INVALID_JWT",
|
|
19
|
+
"UNSUPPORTED_ALG",
|
|
20
|
+
"NO_MATCHING_KEY",
|
|
21
|
+
"INVALID_ISSUER",
|
|
22
|
+
"INVALID_AUDIENCE",
|
|
23
|
+
"INVALID_PATH",
|
|
24
|
+
"INVALID_CREDENTIALS",
|
|
25
|
+
"WEAK_PASSWORD",
|
|
26
|
+
"EMAIL_ALREADY_IN_USE",
|
|
27
|
+
"USER_NOT_FOUND",
|
|
28
|
+
"SESSION_EXPIRED",
|
|
29
|
+
"INVALID_OTP",
|
|
30
|
+
"SESSION_REVOKED"
|
|
31
|
+
]);
|
|
32
|
+
function normalizeErrorCode(code) {
|
|
33
|
+
return code.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
34
|
+
}
|
|
4
35
|
var AuraError = class _AuraError extends Error {
|
|
5
36
|
status;
|
|
6
37
|
code;
|
|
7
38
|
details;
|
|
8
39
|
retryAfter;
|
|
40
|
+
/**
|
|
41
|
+
* Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
|
|
42
|
+
*/
|
|
43
|
+
hint;
|
|
9
44
|
/** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
|
|
10
45
|
__isAuraError = true;
|
|
11
46
|
constructor(status, body, headers) {
|
|
12
|
-
|
|
47
|
+
const serverMessage = body?.error?.message ?? body?.message;
|
|
48
|
+
let rawMessage = serverMessage ?? (status === 429 ? "Too many requests, please wait before retrying." : `HTTP ${status}`);
|
|
13
49
|
let retryAfterSec;
|
|
14
50
|
if (headers) {
|
|
15
51
|
const getHeader = (name) => {
|
|
@@ -24,8 +60,8 @@ var AuraError = class _AuraError extends Error {
|
|
|
24
60
|
const sec = parseInt(ra, 10);
|
|
25
61
|
if (!isNaN(sec)) {
|
|
26
62
|
retryAfterSec = sec;
|
|
27
|
-
if (status === 429) {
|
|
28
|
-
rawMessage = `
|
|
63
|
+
if (status === 429 && !serverMessage) {
|
|
64
|
+
rawMessage = `Too many requests, please wait ${sec} second${sec > 1 ? "s" : ""} before retrying.`;
|
|
29
65
|
}
|
|
30
66
|
}
|
|
31
67
|
}
|
|
@@ -37,46 +73,47 @@ var AuraError = class _AuraError extends Error {
|
|
|
37
73
|
if (retryAfterSec !== void 0) {
|
|
38
74
|
this.retryAfter = retryAfterSec;
|
|
39
75
|
}
|
|
40
|
-
const
|
|
76
|
+
const hint = body?.error?.hint;
|
|
77
|
+
if (typeof hint === "string" && hint) {
|
|
78
|
+
this.hint = hint;
|
|
79
|
+
}
|
|
80
|
+
const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
|
|
81
|
+
const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
|
|
41
82
|
const lowerMsg = rawMessage.toLowerCase();
|
|
42
|
-
if (
|
|
43
|
-
this.code = "
|
|
44
|
-
} else if (
|
|
45
|
-
this.code = "
|
|
46
|
-
} else if (
|
|
47
|
-
this.code = "
|
|
48
|
-
} else if (
|
|
49
|
-
this.code =
|
|
50
|
-
} else if (
|
|
51
|
-
this.code = "
|
|
52
|
-
} else if (
|
|
53
|
-
this.code = "
|
|
54
|
-
} else if (
|
|
55
|
-
this.code = "
|
|
56
|
-
} else if (
|
|
57
|
-
this.code = "
|
|
83
|
+
if (canonical === "USER_ALREADY_EXISTS") {
|
|
84
|
+
this.code = "EMAIL_ALREADY_IN_USE";
|
|
85
|
+
} else if (canonical === "TOKEN_EXPIRED" && status === 401) {
|
|
86
|
+
this.code = "SESSION_EXPIRED";
|
|
87
|
+
} else if (canonical === "RATE_LIMIT_EXCEEDED") {
|
|
88
|
+
this.code = "RATE_LIMITED";
|
|
89
|
+
} else if (canonical && !isGenericCode(canonical)) {
|
|
90
|
+
this.code = canonical;
|
|
91
|
+
} else if (status === 429) {
|
|
92
|
+
this.code = "RATE_LIMITED";
|
|
93
|
+
} else if (status === 401 && lowerMsg.includes("credentials")) {
|
|
94
|
+
this.code = "INVALID_CREDENTIALS";
|
|
95
|
+
} else if (status === 401 && lowerMsg.includes("revoked")) {
|
|
96
|
+
this.code = "SESSION_REVOKED";
|
|
97
|
+
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
98
|
+
this.code = "SESSION_EXPIRED";
|
|
99
|
+
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
100
|
+
this.code = "WEAK_PASSWORD";
|
|
101
|
+
} else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
102
|
+
this.code = "EMAIL_ALREADY_IN_USE";
|
|
103
|
+
} else if (status === 404 && lowerMsg.includes("user not found")) {
|
|
104
|
+
this.code = "USER_NOT_FOUND";
|
|
58
105
|
} else {
|
|
59
|
-
|
|
60
|
-
if (!isGenericHttpCode) {
|
|
61
|
-
this.code = rawCode;
|
|
62
|
-
} else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
|
|
63
|
-
this.code = "invalid_credentials";
|
|
64
|
-
} else if (status === 401 && lowerMsg.includes("r\xE9voqu\xE9e")) {
|
|
65
|
-
this.code = "session_revoked";
|
|
66
|
-
} else if (status === 401 && lowerMsg.includes("expir\xE9")) {
|
|
67
|
-
this.code = "session_expired";
|
|
68
|
-
} else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
|
|
69
|
-
this.code = "weak_password";
|
|
70
|
-
} else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
71
|
-
this.code = "email_already_in_use";
|
|
72
|
-
} else if (status === 404 && lowerMsg.includes("utilisateur")) {
|
|
73
|
-
this.code = "user_not_found";
|
|
74
|
-
} else {
|
|
75
|
-
this.code = rawCode ?? `http_${status}`;
|
|
76
|
-
}
|
|
106
|
+
this.code = canonical ?? `HTTP_${status}`;
|
|
77
107
|
}
|
|
78
108
|
Object.setPrototypeOf(this, _AuraError.prototype);
|
|
79
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
|
|
112
|
+
* Utilisez `error.code`, toujours en SCREAMING_SNAKE.
|
|
113
|
+
*/
|
|
114
|
+
get legacyCode() {
|
|
115
|
+
return LEGACY_LOWERCASE_CODES.has(this.code) ? this.code.toLowerCase() : this.code;
|
|
116
|
+
}
|
|
80
117
|
get isNotFound() {
|
|
81
118
|
return this.status === 404;
|
|
82
119
|
}
|
|
@@ -89,7 +126,17 @@ var AuraError = class _AuraError extends Error {
|
|
|
89
126
|
get isRateLimited() {
|
|
90
127
|
return this.status === 429;
|
|
91
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
|
|
131
|
+
* métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
|
|
132
|
+
*/
|
|
133
|
+
get isQuotaExceeded() {
|
|
134
|
+
return this.code === "QUOTA_EXCEEDED";
|
|
135
|
+
}
|
|
92
136
|
};
|
|
137
|
+
function isGenericCode(code) {
|
|
138
|
+
return code === "UNAUTHORIZED" || code === "BAD_REQUEST" || code === "CONFLICT" || code.startsWith("HTTP_");
|
|
139
|
+
}
|
|
93
140
|
function isAuraError(error) {
|
|
94
141
|
return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
|
|
95
142
|
}
|
|
@@ -245,6 +292,24 @@ function decodeJwtPayload(token) {
|
|
|
245
292
|
return null;
|
|
246
293
|
}
|
|
247
294
|
}
|
|
295
|
+
function userFromAccessToken(token) {
|
|
296
|
+
const c = decodeJwtPayload(token);
|
|
297
|
+
if (!c || typeof c.sub !== "string") return null;
|
|
298
|
+
const meta = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
299
|
+
const amr = Array.isArray(c.amr) ? c.amr : [];
|
|
300
|
+
return {
|
|
301
|
+
id: c.sub,
|
|
302
|
+
email: typeof c.email === "string" ? c.email : null,
|
|
303
|
+
role: "authenticated",
|
|
304
|
+
email_verified: false,
|
|
305
|
+
is_banned: false,
|
|
306
|
+
is_anonymous: amr.some((a) => a?.method === "anonymous"),
|
|
307
|
+
mfa_enabled: c.aal === "aal2",
|
|
308
|
+
user_metadata: meta(c.user_metadata),
|
|
309
|
+
app_metadata: meta(c.app_metadata),
|
|
310
|
+
created_at: ""
|
|
311
|
+
};
|
|
312
|
+
}
|
|
248
313
|
function isTokenExpired(token, marginSec = 0) {
|
|
249
314
|
const payload = decodeJwtPayload(token);
|
|
250
315
|
if (!payload || typeof payload.exp !== "number") return true;
|
|
@@ -354,6 +419,17 @@ var BaseAuthStore = class {
|
|
|
354
419
|
// src/fetch.ts
|
|
355
420
|
var REFRESH_MARGIN_SEC = 30;
|
|
356
421
|
var RETRY_AFTER_CAP_MS = 6e4;
|
|
422
|
+
function networkErrorMessage(err, url) {
|
|
423
|
+
const cause = err?.cause;
|
|
424
|
+
const reason = cause?.code ?? cause?.message ?? (err instanceof Error ? err.message : "request failed");
|
|
425
|
+
let target = url;
|
|
426
|
+
try {
|
|
427
|
+
const u = new URL(url);
|
|
428
|
+
target = `${u.origin}${u.pathname}`;
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
return `Cannot reach ${target} (${reason}). Check the project URL (NEXT_PUBLIC_AURABASE_URL) and your network.`;
|
|
432
|
+
}
|
|
357
433
|
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
358
434
|
function unwrapEnvelope(body) {
|
|
359
435
|
if (body !== null && typeof body === "object" && "data" in body) {
|
|
@@ -475,7 +551,7 @@ async function doRefresh(client) {
|
|
|
475
551
|
error: new AuraError(0, {
|
|
476
552
|
error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
|
|
477
553
|
code: "network_error",
|
|
478
|
-
message: err
|
|
554
|
+
message: networkErrorMessage(err, `${client.baseUrl}/v1/auth/refresh`)
|
|
479
555
|
}
|
|
480
556
|
})
|
|
481
557
|
};
|
|
@@ -676,7 +752,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
676
752
|
error: new AuraError(0, {
|
|
677
753
|
error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
|
|
678
754
|
code: "network_error",
|
|
679
|
-
message: err
|
|
755
|
+
message: networkErrorMessage(err, url)
|
|
680
756
|
}
|
|
681
757
|
})
|
|
682
758
|
};
|
|
@@ -825,13 +901,16 @@ function parseCookieHeader(header) {
|
|
|
825
901
|
}
|
|
826
902
|
return result;
|
|
827
903
|
}
|
|
904
|
+
function sameSiteAttribute(v) {
|
|
905
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
906
|
+
}
|
|
828
907
|
function serializeCookieHeader(name, value, options = {}) {
|
|
829
908
|
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
830
909
|
if (options.path) parts.push(`Path=${options.path}`);
|
|
831
910
|
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
832
911
|
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
833
912
|
if (options.secure) parts.push("Secure");
|
|
834
|
-
if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
|
|
913
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
835
914
|
if (options.httpOnly) parts.push("HttpOnly");
|
|
836
915
|
return parts.join("; ");
|
|
837
916
|
}
|
|
@@ -922,6 +1001,9 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
922
1001
|
/**
|
|
923
1002
|
* Populate the store from a raw `Cookie` header string (server-side).
|
|
924
1003
|
* Looks for the key matching `this.key`.
|
|
1004
|
+
*
|
|
1005
|
+
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
1006
|
+
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
925
1007
|
*/
|
|
926
1008
|
loadFromCookie(cookieString) {
|
|
927
1009
|
const prefix = `${this.key}=`;
|
|
@@ -930,20 +1012,22 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
930
1012
|
try {
|
|
931
1013
|
const raw = decodeURIComponent(entry.slice(prefix.length));
|
|
932
1014
|
const parsed = JSON.parse(raw);
|
|
933
|
-
|
|
934
|
-
|
|
1015
|
+
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
1016
|
+
if (parsed.token && parsed.refreshToken && user) {
|
|
1017
|
+
super.save(parsed.token, parsed.refreshToken, user);
|
|
935
1018
|
}
|
|
936
1019
|
} catch {
|
|
937
1020
|
}
|
|
938
1021
|
}
|
|
939
1022
|
/**
|
|
940
|
-
* Serialize
|
|
1023
|
+
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
1024
|
+
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
1025
|
+
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
941
1026
|
*/
|
|
942
1027
|
exportToCookie(options = {}) {
|
|
943
1028
|
const data = {
|
|
944
1029
|
token: this.token,
|
|
945
|
-
refreshToken: this.refreshToken
|
|
946
|
-
user: this.user
|
|
1030
|
+
refreshToken: this.refreshToken
|
|
947
1031
|
};
|
|
948
1032
|
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
949
1033
|
const parts = [`${this.key}=${encoded}`];
|
|
@@ -951,7 +1035,7 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
951
1035
|
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
952
1036
|
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
953
1037
|
if (options.secure) parts.push("Secure");
|
|
954
|
-
if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
|
|
1038
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
955
1039
|
if (options.httpOnly) parts.push("HttpOnly");
|
|
956
1040
|
return parts.join("; ");
|
|
957
1041
|
}
|
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 = '
|
|
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.
|
|
@@ -24,19 +24,35 @@ declare class AuraError extends Error {
|
|
|
24
24
|
readonly code: AuraErrorCode;
|
|
25
25
|
readonly details: unknown;
|
|
26
26
|
readonly retryAfter?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
|
|
29
|
+
*/
|
|
30
|
+
readonly hint?: string;
|
|
27
31
|
/** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
|
|
28
32
|
protected readonly __isAuraError = true;
|
|
29
33
|
constructor(status: number, body?: {
|
|
30
34
|
error?: {
|
|
31
35
|
code?: string;
|
|
32
36
|
message?: string;
|
|
37
|
+
hint?: string;
|
|
38
|
+
details?: unknown;
|
|
33
39
|
};
|
|
34
40
|
message?: string;
|
|
35
41
|
} | null, headers?: Headers | Record<string, string | null | undefined>);
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
|
|
44
|
+
* Utilisez `error.code`, toujours en SCREAMING_SNAKE.
|
|
45
|
+
*/
|
|
46
|
+
get legacyCode(): string;
|
|
36
47
|
get isNotFound(): boolean;
|
|
37
48
|
get isUnauthorized(): boolean;
|
|
38
49
|
get isForbidden(): boolean;
|
|
39
50
|
get isRateLimited(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
|
|
53
|
+
* métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
|
|
54
|
+
*/
|
|
55
|
+
get isQuotaExceeded(): boolean;
|
|
40
56
|
}
|
|
41
57
|
declare function isAuraError(error: unknown): error is AuraError;
|
|
42
58
|
|
|
@@ -189,10 +205,6 @@ interface User {
|
|
|
189
205
|
interface Session {
|
|
190
206
|
access_token: string;
|
|
191
207
|
refresh_token: string;
|
|
192
|
-
/** Alias camelCase pour parité DX Supabase/JS */
|
|
193
|
-
accessToken?: string;
|
|
194
|
-
/** Alias camelCase pour parité DX Supabase/JS */
|
|
195
|
-
refreshToken?: string;
|
|
196
208
|
token_type: string;
|
|
197
209
|
expires_in: number;
|
|
198
210
|
expires_at?: number;
|
|
@@ -203,30 +215,20 @@ interface Session {
|
|
|
203
215
|
interface TokenPair {
|
|
204
216
|
access_token: string;
|
|
205
217
|
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
218
|
token_type: string;
|
|
211
219
|
expires_in: number;
|
|
212
220
|
user: User;
|
|
213
221
|
provider_token?: string | null;
|
|
214
222
|
provider_refresh_token?: string | null;
|
|
215
|
-
/** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
/** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
|
|
224
|
+
* `null` après une inscription sur un projet qui exige un e-mail vérifié (AUTH-002). */
|
|
225
|
+
session?: Session | null;
|
|
218
226
|
}
|
|
219
227
|
interface MfaChallenge {
|
|
220
228
|
mfa_required: true;
|
|
221
229
|
mfa_token: string;
|
|
222
230
|
mfa_setup_required?: boolean;
|
|
223
231
|
session?: undefined;
|
|
224
|
-
/** Alias ergonomique aligné sur la documentation officielle */
|
|
225
|
-
mfaChallenge?: {
|
|
226
|
-
mfaRequired: boolean;
|
|
227
|
-
mfaToken: string;
|
|
228
|
-
mfaSetupRequired?: boolean;
|
|
229
|
-
};
|
|
230
232
|
}
|
|
231
233
|
type LoginResponse = TokenPair | MfaChallenge;
|
|
232
234
|
type AuthEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'MFA_CHALLENGE' | 'PASSWORD_RECOVERY';
|
|
@@ -361,9 +363,35 @@ interface MfaEnrollResponse {
|
|
|
361
363
|
otpauth_url: string;
|
|
362
364
|
secret: string;
|
|
363
365
|
}
|
|
364
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Relève un défi MFA : exactement UN facteur parmi `code` (TOTP à 6 chiffres), `recovery_code`
|
|
368
|
+
* (code de secours `XXXXXXXX-XXXXXXXX`) et `sms_code` (code reçu après `auth.mfa.sms.send`).
|
|
369
|
+
* Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
|
|
370
|
+
*/
|
|
371
|
+
type MfaVerifyRequest = {
|
|
365
372
|
mfa_token: string;
|
|
366
373
|
code: string;
|
|
374
|
+
} | {
|
|
375
|
+
mfa_token: string;
|
|
376
|
+
recovery_code: string;
|
|
377
|
+
} | {
|
|
378
|
+
mfa_token: string;
|
|
379
|
+
sms_code: string;
|
|
380
|
+
};
|
|
381
|
+
/** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
|
|
382
|
+
type MfaFactorType = 'totp' | 'webauthn' | 'sms';
|
|
383
|
+
/** Passkey WebAuthn enrôlée (`auth.mfa.webauthn.list`). */
|
|
384
|
+
interface PasskeyInfo {
|
|
385
|
+
id: string;
|
|
386
|
+
friendly_name: string;
|
|
387
|
+
created_at: string;
|
|
388
|
+
last_used_at: string | null;
|
|
389
|
+
}
|
|
390
|
+
/** 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. */
|
|
391
|
+
interface PasskeyRegisterResult {
|
|
392
|
+
id: string;
|
|
393
|
+
friendly_name: string;
|
|
394
|
+
recovery_codes?: string[];
|
|
367
395
|
}
|
|
368
396
|
interface SessionInfo {
|
|
369
397
|
id: string;
|
|
@@ -1490,6 +1518,11 @@ interface AuraClientOptions {
|
|
|
1490
1518
|
* Défaut : `true` en navigateur, no-op hors navigateur (SSR/Node).
|
|
1491
1519
|
*/
|
|
1492
1520
|
detectSessionInUrl?: boolean;
|
|
1521
|
+
/**
|
|
1522
|
+
* AUTH-015 — flux OAuth par défaut de `signInWithOAuth` pour ce client (`'pkce'` recommandé
|
|
1523
|
+
* en navigateur). Une option `flowType` passée à l'appel reste prioritaire.
|
|
1524
|
+
*/
|
|
1525
|
+
flowType?: 'pkce' | 'implicit';
|
|
1493
1526
|
};
|
|
1494
1527
|
fetch?: typeof globalThis.fetch;
|
|
1495
1528
|
/**
|
|
@@ -1773,7 +1806,7 @@ interface AdminUpdateUserRequest {
|
|
|
1773
1806
|
mfa_enabled?: boolean;
|
|
1774
1807
|
revoke_sessions?: boolean;
|
|
1775
1808
|
}
|
|
1776
|
-
type GenerateLinkType = 'magic' | 'recovery' | 'invite';
|
|
1809
|
+
type GenerateLinkType = 'signup' | 'magic' | 'recovery' | 'invite';
|
|
1777
1810
|
interface GenerateLinkResult {
|
|
1778
1811
|
link: string;
|
|
1779
1812
|
token: string;
|
|
@@ -1927,7 +1960,8 @@ interface SerializeCookieOptions {
|
|
|
1927
1960
|
domain?: string;
|
|
1928
1961
|
maxAge?: number;
|
|
1929
1962
|
secure?: boolean;
|
|
1930
|
-
|
|
1963
|
+
/** Minuscules : forme acceptée par `ResponseCookie` de Next.js (`'lax' | 'strict' | 'none'`). */
|
|
1964
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
1931
1965
|
httpOnly?: boolean;
|
|
1932
1966
|
}
|
|
1933
1967
|
/**
|
|
@@ -1935,7 +1969,6 @@ interface SerializeCookieOptions {
|
|
|
1935
1969
|
*/
|
|
1936
1970
|
declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
|
|
1937
1971
|
|
|
1938
|
-
/** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
|
|
1939
1972
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
1940
1973
|
/** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
|
|
1941
1974
|
declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
|
|
@@ -2010,7 +2043,7 @@ interface CookieOptions {
|
|
|
2010
2043
|
domain?: string;
|
|
2011
2044
|
maxAge?: number;
|
|
2012
2045
|
secure?: boolean;
|
|
2013
|
-
sameSite?: '
|
|
2046
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
2014
2047
|
httpOnly?: boolean;
|
|
2015
2048
|
}
|
|
2016
2049
|
/**
|
|
@@ -2053,10 +2086,15 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2053
2086
|
/**
|
|
2054
2087
|
* Populate the store from a raw `Cookie` header string (server-side).
|
|
2055
2088
|
* Looks for the key matching `this.key`.
|
|
2089
|
+
*
|
|
2090
|
+
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
2091
|
+
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
2056
2092
|
*/
|
|
2057
2093
|
loadFromCookie(cookieString: string): void;
|
|
2058
2094
|
/**
|
|
2059
|
-
* Serialize
|
|
2095
|
+
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
2096
|
+
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
2097
|
+
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
2060
2098
|
*/
|
|
2061
2099
|
exportToCookie(options?: CookieOptions): string;
|
|
2062
2100
|
private _getStorage;
|
|
@@ -2065,4 +2103,4 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2065
2103
|
private _removeStorage;
|
|
2066
2104
|
}
|
|
2067
2105
|
|
|
2068
|
-
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 };
|
|
2106
|
+
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 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 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 = '
|
|
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.
|
|
@@ -24,19 +24,35 @@ declare class AuraError extends Error {
|
|
|
24
24
|
readonly code: AuraErrorCode;
|
|
25
25
|
readonly details: unknown;
|
|
26
26
|
readonly retryAfter?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
|
|
29
|
+
*/
|
|
30
|
+
readonly hint?: string;
|
|
27
31
|
/** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
|
|
28
32
|
protected readonly __isAuraError = true;
|
|
29
33
|
constructor(status: number, body?: {
|
|
30
34
|
error?: {
|
|
31
35
|
code?: string;
|
|
32
36
|
message?: string;
|
|
37
|
+
hint?: string;
|
|
38
|
+
details?: unknown;
|
|
33
39
|
};
|
|
34
40
|
message?: string;
|
|
35
41
|
} | null, headers?: Headers | Record<string, string | null | undefined>);
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
|
|
44
|
+
* Utilisez `error.code`, toujours en SCREAMING_SNAKE.
|
|
45
|
+
*/
|
|
46
|
+
get legacyCode(): string;
|
|
36
47
|
get isNotFound(): boolean;
|
|
37
48
|
get isUnauthorized(): boolean;
|
|
38
49
|
get isForbidden(): boolean;
|
|
39
50
|
get isRateLimited(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
|
|
53
|
+
* métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
|
|
54
|
+
*/
|
|
55
|
+
get isQuotaExceeded(): boolean;
|
|
40
56
|
}
|
|
41
57
|
declare function isAuraError(error: unknown): error is AuraError;
|
|
42
58
|
|
|
@@ -189,10 +205,6 @@ interface User {
|
|
|
189
205
|
interface Session {
|
|
190
206
|
access_token: string;
|
|
191
207
|
refresh_token: string;
|
|
192
|
-
/** Alias camelCase pour parité DX Supabase/JS */
|
|
193
|
-
accessToken?: string;
|
|
194
|
-
/** Alias camelCase pour parité DX Supabase/JS */
|
|
195
|
-
refreshToken?: string;
|
|
196
208
|
token_type: string;
|
|
197
209
|
expires_in: number;
|
|
198
210
|
expires_at?: number;
|
|
@@ -203,30 +215,20 @@ interface Session {
|
|
|
203
215
|
interface TokenPair {
|
|
204
216
|
access_token: string;
|
|
205
217
|
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
218
|
token_type: string;
|
|
211
219
|
expires_in: number;
|
|
212
220
|
user: User;
|
|
213
221
|
provider_token?: string | null;
|
|
214
222
|
provider_refresh_token?: string | null;
|
|
215
|
-
/** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
|
|
216
|
-
|
|
217
|
-
|
|
223
|
+
/** Sous-objet session pour parité DX Supabase (`data.session.access_token`).
|
|
224
|
+
* `null` après une inscription sur un projet qui exige un e-mail vérifié (AUTH-002). */
|
|
225
|
+
session?: Session | null;
|
|
218
226
|
}
|
|
219
227
|
interface MfaChallenge {
|
|
220
228
|
mfa_required: true;
|
|
221
229
|
mfa_token: string;
|
|
222
230
|
mfa_setup_required?: boolean;
|
|
223
231
|
session?: undefined;
|
|
224
|
-
/** Alias ergonomique aligné sur la documentation officielle */
|
|
225
|
-
mfaChallenge?: {
|
|
226
|
-
mfaRequired: boolean;
|
|
227
|
-
mfaToken: string;
|
|
228
|
-
mfaSetupRequired?: boolean;
|
|
229
|
-
};
|
|
230
232
|
}
|
|
231
233
|
type LoginResponse = TokenPair | MfaChallenge;
|
|
232
234
|
type AuthEvent = 'INITIAL_SESSION' | 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED' | 'MFA_CHALLENGE' | 'PASSWORD_RECOVERY';
|
|
@@ -361,9 +363,35 @@ interface MfaEnrollResponse {
|
|
|
361
363
|
otpauth_url: string;
|
|
362
364
|
secret: string;
|
|
363
365
|
}
|
|
364
|
-
|
|
366
|
+
/**
|
|
367
|
+
* Relève un défi MFA : exactement UN facteur parmi `code` (TOTP à 6 chiffres), `recovery_code`
|
|
368
|
+
* (code de secours `XXXXXXXX-XXXXXXXX`) et `sms_code` (code reçu après `auth.mfa.sms.send`).
|
|
369
|
+
* Pour une passkey, utilisez `auth.mfa.webauthn.verify`.
|
|
370
|
+
*/
|
|
371
|
+
type MfaVerifyRequest = {
|
|
365
372
|
mfa_token: string;
|
|
366
373
|
code: string;
|
|
374
|
+
} | {
|
|
375
|
+
mfa_token: string;
|
|
376
|
+
recovery_code: string;
|
|
377
|
+
} | {
|
|
378
|
+
mfa_token: string;
|
|
379
|
+
sms_code: string;
|
|
380
|
+
};
|
|
381
|
+
/** Facteur MFA utilisable pour relever un défi (`auth.mfa.factors`). */
|
|
382
|
+
type MfaFactorType = 'totp' | 'webauthn' | 'sms';
|
|
383
|
+
/** Passkey WebAuthn enrôlée (`auth.mfa.webauthn.list`). */
|
|
384
|
+
interface PasskeyInfo {
|
|
385
|
+
id: string;
|
|
386
|
+
friendly_name: string;
|
|
387
|
+
created_at: string;
|
|
388
|
+
last_used_at: string | null;
|
|
389
|
+
}
|
|
390
|
+
/** 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. */
|
|
391
|
+
interface PasskeyRegisterResult {
|
|
392
|
+
id: string;
|
|
393
|
+
friendly_name: string;
|
|
394
|
+
recovery_codes?: string[];
|
|
367
395
|
}
|
|
368
396
|
interface SessionInfo {
|
|
369
397
|
id: string;
|
|
@@ -1490,6 +1518,11 @@ interface AuraClientOptions {
|
|
|
1490
1518
|
* Défaut : `true` en navigateur, no-op hors navigateur (SSR/Node).
|
|
1491
1519
|
*/
|
|
1492
1520
|
detectSessionInUrl?: boolean;
|
|
1521
|
+
/**
|
|
1522
|
+
* AUTH-015 — flux OAuth par défaut de `signInWithOAuth` pour ce client (`'pkce'` recommandé
|
|
1523
|
+
* en navigateur). Une option `flowType` passée à l'appel reste prioritaire.
|
|
1524
|
+
*/
|
|
1525
|
+
flowType?: 'pkce' | 'implicit';
|
|
1493
1526
|
};
|
|
1494
1527
|
fetch?: typeof globalThis.fetch;
|
|
1495
1528
|
/**
|
|
@@ -1773,7 +1806,7 @@ interface AdminUpdateUserRequest {
|
|
|
1773
1806
|
mfa_enabled?: boolean;
|
|
1774
1807
|
revoke_sessions?: boolean;
|
|
1775
1808
|
}
|
|
1776
|
-
type GenerateLinkType = 'magic' | 'recovery' | 'invite';
|
|
1809
|
+
type GenerateLinkType = 'signup' | 'magic' | 'recovery' | 'invite';
|
|
1777
1810
|
interface GenerateLinkResult {
|
|
1778
1811
|
link: string;
|
|
1779
1812
|
token: string;
|
|
@@ -1927,7 +1960,8 @@ interface SerializeCookieOptions {
|
|
|
1927
1960
|
domain?: string;
|
|
1928
1961
|
maxAge?: number;
|
|
1929
1962
|
secure?: boolean;
|
|
1930
|
-
|
|
1963
|
+
/** Minuscules : forme acceptée par `ResponseCookie` de Next.js (`'lax' | 'strict' | 'none'`). */
|
|
1964
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
1931
1965
|
httpOnly?: boolean;
|
|
1932
1966
|
}
|
|
1933
1967
|
/**
|
|
@@ -1935,7 +1969,6 @@ interface SerializeCookieOptions {
|
|
|
1935
1969
|
*/
|
|
1936
1970
|
declare function serializeCookieHeader(name: string, value: string, options?: SerializeCookieOptions): string;
|
|
1937
1971
|
|
|
1938
|
-
/** Timeout par défaut d'une requête HTTP (borne un serveur qui « hang »). */
|
|
1939
1972
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
1940
1973
|
/** Point d'entrée unique du refresh, dédupliqué par client (single-flight). */
|
|
1941
1974
|
declare function refreshOnce(client: AuraClientLike): Promise<AuraResponse<TokenPair>>;
|
|
@@ -2010,7 +2043,7 @@ interface CookieOptions {
|
|
|
2010
2043
|
domain?: string;
|
|
2011
2044
|
maxAge?: number;
|
|
2012
2045
|
secure?: boolean;
|
|
2013
|
-
sameSite?: '
|
|
2046
|
+
sameSite?: 'strict' | 'lax' | 'none';
|
|
2014
2047
|
httpOnly?: boolean;
|
|
2015
2048
|
}
|
|
2016
2049
|
/**
|
|
@@ -2053,10 +2086,15 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2053
2086
|
/**
|
|
2054
2087
|
* Populate the store from a raw `Cookie` header string (server-side).
|
|
2055
2088
|
* Looks for the key matching `this.key`.
|
|
2089
|
+
*
|
|
2090
|
+
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
2091
|
+
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
2056
2092
|
*/
|
|
2057
2093
|
loadFromCookie(cookieString: string): void;
|
|
2058
2094
|
/**
|
|
2059
|
-
* Serialize
|
|
2095
|
+
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
2096
|
+
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
2097
|
+
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
2060
2098
|
*/
|
|
2061
2099
|
exportToCookie(options?: CookieOptions): string;
|
|
2062
2100
|
private _getStorage;
|
|
@@ -2065,4 +2103,4 @@ declare class LocalAuthStore extends BaseAuthStore {
|
|
|
2065
2103
|
private _removeStorage;
|
|
2066
2104
|
}
|
|
2067
2105
|
|
|
2068
|
-
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 };
|
|
2106
|
+
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 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 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
|
@@ -1,13 +1,49 @@
|
|
|
1
1
|
// src/AuraError.ts
|
|
2
|
+
var LEGACY_LOWERCASE_CODES = /* @__PURE__ */ new Set([
|
|
3
|
+
"NO_SESSION",
|
|
4
|
+
"TIMEOUT",
|
|
5
|
+
"NETWORK_ERROR",
|
|
6
|
+
"INVALID_RESPONSE",
|
|
7
|
+
"REFRESH_DISCARDED",
|
|
8
|
+
"UNVERIFIABLE_TOKEN",
|
|
9
|
+
"NO_CODE_VERIFIER",
|
|
10
|
+
"PKCE_GENERATION_FAILED",
|
|
11
|
+
"MISSING_EMAIL",
|
|
12
|
+
"JWKS_FETCH_FAILED",
|
|
13
|
+
"SIGNATURE_INVALID",
|
|
14
|
+
"TOKEN_EXPIRED",
|
|
15
|
+
"TOKEN_NOT_YET_VALID",
|
|
16
|
+
"INVALID_JWT",
|
|
17
|
+
"UNSUPPORTED_ALG",
|
|
18
|
+
"NO_MATCHING_KEY",
|
|
19
|
+
"INVALID_ISSUER",
|
|
20
|
+
"INVALID_AUDIENCE",
|
|
21
|
+
"INVALID_PATH",
|
|
22
|
+
"INVALID_CREDENTIALS",
|
|
23
|
+
"WEAK_PASSWORD",
|
|
24
|
+
"EMAIL_ALREADY_IN_USE",
|
|
25
|
+
"USER_NOT_FOUND",
|
|
26
|
+
"SESSION_EXPIRED",
|
|
27
|
+
"INVALID_OTP",
|
|
28
|
+
"SESSION_REVOKED"
|
|
29
|
+
]);
|
|
30
|
+
function normalizeErrorCode(code) {
|
|
31
|
+
return code.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
|
|
32
|
+
}
|
|
2
33
|
var AuraError = class _AuraError extends Error {
|
|
3
34
|
status;
|
|
4
35
|
code;
|
|
5
36
|
details;
|
|
6
37
|
retryAfter;
|
|
38
|
+
/**
|
|
39
|
+
* Piste de correction fournie par le serveur (ex. refus RLS : quelle politique créer). J1-010.
|
|
40
|
+
*/
|
|
41
|
+
hint;
|
|
7
42
|
/** Marqueur runtime pour `isAuraError` (détection cross-realm/cross-bundle). */
|
|
8
43
|
__isAuraError = true;
|
|
9
44
|
constructor(status, body, headers) {
|
|
10
|
-
|
|
45
|
+
const serverMessage = body?.error?.message ?? body?.message;
|
|
46
|
+
let rawMessage = serverMessage ?? (status === 429 ? "Too many requests, please wait before retrying." : `HTTP ${status}`);
|
|
11
47
|
let retryAfterSec;
|
|
12
48
|
if (headers) {
|
|
13
49
|
const getHeader = (name) => {
|
|
@@ -22,8 +58,8 @@ var AuraError = class _AuraError extends Error {
|
|
|
22
58
|
const sec = parseInt(ra, 10);
|
|
23
59
|
if (!isNaN(sec)) {
|
|
24
60
|
retryAfterSec = sec;
|
|
25
|
-
if (status === 429) {
|
|
26
|
-
rawMessage = `
|
|
61
|
+
if (status === 429 && !serverMessage) {
|
|
62
|
+
rawMessage = `Too many requests, please wait ${sec} second${sec > 1 ? "s" : ""} before retrying.`;
|
|
27
63
|
}
|
|
28
64
|
}
|
|
29
65
|
}
|
|
@@ -35,46 +71,47 @@ var AuraError = class _AuraError extends Error {
|
|
|
35
71
|
if (retryAfterSec !== void 0) {
|
|
36
72
|
this.retryAfter = retryAfterSec;
|
|
37
73
|
}
|
|
38
|
-
const
|
|
74
|
+
const hint = body?.error?.hint;
|
|
75
|
+
if (typeof hint === "string" && hint) {
|
|
76
|
+
this.hint = hint;
|
|
77
|
+
}
|
|
78
|
+
const rawCode = typeof body?.error?.code === "string" ? body.error.code : void 0;
|
|
79
|
+
const canonical = rawCode ? normalizeErrorCode(rawCode) : void 0;
|
|
39
80
|
const lowerMsg = rawMessage.toLowerCase();
|
|
40
|
-
if (
|
|
41
|
-
this.code = "
|
|
42
|
-
} else if (
|
|
43
|
-
this.code = "
|
|
44
|
-
} else if (
|
|
45
|
-
this.code = "
|
|
46
|
-
} else if (
|
|
47
|
-
this.code =
|
|
48
|
-
} else if (
|
|
49
|
-
this.code = "
|
|
50
|
-
} else if (
|
|
51
|
-
this.code = "
|
|
52
|
-
} else if (
|
|
53
|
-
this.code = "
|
|
54
|
-
} else if (
|
|
55
|
-
this.code = "
|
|
81
|
+
if (canonical === "USER_ALREADY_EXISTS") {
|
|
82
|
+
this.code = "EMAIL_ALREADY_IN_USE";
|
|
83
|
+
} else if (canonical === "TOKEN_EXPIRED" && status === 401) {
|
|
84
|
+
this.code = "SESSION_EXPIRED";
|
|
85
|
+
} else if (canonical === "RATE_LIMIT_EXCEEDED") {
|
|
86
|
+
this.code = "RATE_LIMITED";
|
|
87
|
+
} else if (canonical && !isGenericCode(canonical)) {
|
|
88
|
+
this.code = canonical;
|
|
89
|
+
} else if (status === 429) {
|
|
90
|
+
this.code = "RATE_LIMITED";
|
|
91
|
+
} else if (status === 401 && lowerMsg.includes("credentials")) {
|
|
92
|
+
this.code = "INVALID_CREDENTIALS";
|
|
93
|
+
} else if (status === 401 && lowerMsg.includes("revoked")) {
|
|
94
|
+
this.code = "SESSION_REVOKED";
|
|
95
|
+
} else if (status === 401 && lowerMsg.includes("expired")) {
|
|
96
|
+
this.code = "SESSION_EXPIRED";
|
|
97
|
+
} else if (status === 400 && lowerMsg.includes("password")) {
|
|
98
|
+
this.code = "WEAK_PASSWORD";
|
|
99
|
+
} else if (status === 409 && (lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
100
|
+
this.code = "EMAIL_ALREADY_IN_USE";
|
|
101
|
+
} else if (status === 404 && lowerMsg.includes("user not found")) {
|
|
102
|
+
this.code = "USER_NOT_FOUND";
|
|
56
103
|
} else {
|
|
57
|
-
|
|
58
|
-
if (!isGenericHttpCode) {
|
|
59
|
-
this.code = rawCode;
|
|
60
|
-
} else if (status === 401 && (lowerMsg.includes("identifiants") || lowerMsg.includes("credentials") || lowerMsg.includes("mot de passe incorrect"))) {
|
|
61
|
-
this.code = "invalid_credentials";
|
|
62
|
-
} else if (status === 401 && lowerMsg.includes("r\xE9voqu\xE9e")) {
|
|
63
|
-
this.code = "session_revoked";
|
|
64
|
-
} else if (status === 401 && lowerMsg.includes("expir\xE9")) {
|
|
65
|
-
this.code = "session_expired";
|
|
66
|
-
} else if (status === 400 && (lowerMsg.includes("mot de passe") || lowerMsg.includes("password"))) {
|
|
67
|
-
this.code = "weak_password";
|
|
68
|
-
} else if (status === 409 && (lowerMsg.includes("d\xE9j\xE0 utilis\xE9") || lowerMsg.includes("already exists") || lowerMsg.includes("already in use") || lowerMsg.includes("email"))) {
|
|
69
|
-
this.code = "email_already_in_use";
|
|
70
|
-
} else if (status === 404 && lowerMsg.includes("utilisateur")) {
|
|
71
|
-
this.code = "user_not_found";
|
|
72
|
-
} else {
|
|
73
|
-
this.code = rawCode ?? `http_${status}`;
|
|
74
|
-
}
|
|
104
|
+
this.code = canonical ?? `HTTP_${status}`;
|
|
75
105
|
}
|
|
76
106
|
Object.setPrototypeOf(this, _AuraError.prototype);
|
|
77
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* @deprecated Ancienne forme en minuscules de `code` (`invalid_credentials`, `no_session`…).
|
|
110
|
+
* Utilisez `error.code`, toujours en SCREAMING_SNAKE.
|
|
111
|
+
*/
|
|
112
|
+
get legacyCode() {
|
|
113
|
+
return LEGACY_LOWERCASE_CODES.has(this.code) ? this.code.toLowerCase() : this.code;
|
|
114
|
+
}
|
|
78
115
|
get isNotFound() {
|
|
79
116
|
return this.status === 404;
|
|
80
117
|
}
|
|
@@ -87,7 +124,17 @@ var AuraError = class _AuraError extends Error {
|
|
|
87
124
|
get isRateLimited() {
|
|
88
125
|
return this.status === 429;
|
|
89
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Quota mensuel du plan atteint (HTTP 403, `code: 'QUOTA_EXCEEDED'`). Le message nomme la
|
|
129
|
+
* métrique, l'usage, la limite et la date de remise à zéro ; ce n'est pas un refus d'autorisation.
|
|
130
|
+
*/
|
|
131
|
+
get isQuotaExceeded() {
|
|
132
|
+
return this.code === "QUOTA_EXCEEDED";
|
|
133
|
+
}
|
|
90
134
|
};
|
|
135
|
+
function isGenericCode(code) {
|
|
136
|
+
return code === "UNAUTHORIZED" || code === "BAD_REQUEST" || code === "CONFLICT" || code.startsWith("HTTP_");
|
|
137
|
+
}
|
|
91
138
|
function isAuraError(error) {
|
|
92
139
|
return error instanceof AuraError || typeof error === "object" && error !== null && error.__isAuraError === true;
|
|
93
140
|
}
|
|
@@ -243,6 +290,24 @@ function decodeJwtPayload(token) {
|
|
|
243
290
|
return null;
|
|
244
291
|
}
|
|
245
292
|
}
|
|
293
|
+
function userFromAccessToken(token) {
|
|
294
|
+
const c = decodeJwtPayload(token);
|
|
295
|
+
if (!c || typeof c.sub !== "string") return null;
|
|
296
|
+
const meta = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : {};
|
|
297
|
+
const amr = Array.isArray(c.amr) ? c.amr : [];
|
|
298
|
+
return {
|
|
299
|
+
id: c.sub,
|
|
300
|
+
email: typeof c.email === "string" ? c.email : null,
|
|
301
|
+
role: "authenticated",
|
|
302
|
+
email_verified: false,
|
|
303
|
+
is_banned: false,
|
|
304
|
+
is_anonymous: amr.some((a) => a?.method === "anonymous"),
|
|
305
|
+
mfa_enabled: c.aal === "aal2",
|
|
306
|
+
user_metadata: meta(c.user_metadata),
|
|
307
|
+
app_metadata: meta(c.app_metadata),
|
|
308
|
+
created_at: ""
|
|
309
|
+
};
|
|
310
|
+
}
|
|
246
311
|
function isTokenExpired(token, marginSec = 0) {
|
|
247
312
|
const payload = decodeJwtPayload(token);
|
|
248
313
|
if (!payload || typeof payload.exp !== "number") return true;
|
|
@@ -352,6 +417,17 @@ var BaseAuthStore = class {
|
|
|
352
417
|
// src/fetch.ts
|
|
353
418
|
var REFRESH_MARGIN_SEC = 30;
|
|
354
419
|
var RETRY_AFTER_CAP_MS = 6e4;
|
|
420
|
+
function networkErrorMessage(err, url) {
|
|
421
|
+
const cause = err?.cause;
|
|
422
|
+
const reason = cause?.code ?? cause?.message ?? (err instanceof Error ? err.message : "request failed");
|
|
423
|
+
let target = url;
|
|
424
|
+
try {
|
|
425
|
+
const u = new URL(url);
|
|
426
|
+
target = `${u.origin}${u.pathname}`;
|
|
427
|
+
} catch {
|
|
428
|
+
}
|
|
429
|
+
return `Cannot reach ${target} (${reason}). Check the project URL (NEXT_PUBLIC_AURABASE_URL) and your network.`;
|
|
430
|
+
}
|
|
355
431
|
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
356
432
|
function unwrapEnvelope(body) {
|
|
357
433
|
if (body !== null && typeof body === "object" && "data" in body) {
|
|
@@ -473,7 +549,7 @@ async function doRefresh(client) {
|
|
|
473
549
|
error: new AuraError(0, {
|
|
474
550
|
error: timedOut ? { code: "timeout", message: `Refresh timed out after ${timeoutMs}ms` } : {
|
|
475
551
|
code: "network_error",
|
|
476
|
-
message: err
|
|
552
|
+
message: networkErrorMessage(err, `${client.baseUrl}/v1/auth/refresh`)
|
|
477
553
|
}
|
|
478
554
|
})
|
|
479
555
|
};
|
|
@@ -674,7 +750,7 @@ async function fetchWithAuth(url, init, client) {
|
|
|
674
750
|
error: new AuraError(0, {
|
|
675
751
|
error: timedOut ? { code: "timeout", message: `Request timed out after ${timeoutMs}ms` } : {
|
|
676
752
|
code: "network_error",
|
|
677
|
-
message: err
|
|
753
|
+
message: networkErrorMessage(err, url)
|
|
678
754
|
}
|
|
679
755
|
})
|
|
680
756
|
};
|
|
@@ -823,13 +899,16 @@ function parseCookieHeader(header) {
|
|
|
823
899
|
}
|
|
824
900
|
return result;
|
|
825
901
|
}
|
|
902
|
+
function sameSiteAttribute(v) {
|
|
903
|
+
return v.charAt(0).toUpperCase() + v.slice(1);
|
|
904
|
+
}
|
|
826
905
|
function serializeCookieHeader(name, value, options = {}) {
|
|
827
906
|
const parts = [`${name}=${encodeURIComponent(value)}`];
|
|
828
907
|
if (options.path) parts.push(`Path=${options.path}`);
|
|
829
908
|
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
830
909
|
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
831
910
|
if (options.secure) parts.push("Secure");
|
|
832
|
-
if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
|
|
911
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
833
912
|
if (options.httpOnly) parts.push("HttpOnly");
|
|
834
913
|
return parts.join("; ");
|
|
835
914
|
}
|
|
@@ -920,6 +999,9 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
920
999
|
/**
|
|
921
1000
|
* Populate the store from a raw `Cookie` header string (server-side).
|
|
922
1001
|
* Looks for the key matching `this.key`.
|
|
1002
|
+
*
|
|
1003
|
+
* Le cookie ne porte que `{ token, refreshToken }` (AUTH-036) : l'utilisateur est reconstruit à partir des
|
|
1004
|
+
* claims du jeton (non vérifié — `auth.getUser()` le fait vérifier par le serveur).
|
|
923
1005
|
*/
|
|
924
1006
|
loadFromCookie(cookieString) {
|
|
925
1007
|
const prefix = `${this.key}=`;
|
|
@@ -928,20 +1010,22 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
928
1010
|
try {
|
|
929
1011
|
const raw = decodeURIComponent(entry.slice(prefix.length));
|
|
930
1012
|
const parsed = JSON.parse(raw);
|
|
931
|
-
|
|
932
|
-
|
|
1013
|
+
const user = parsed.token ? userFromAccessToken(parsed.token) : null;
|
|
1014
|
+
if (parsed.token && parsed.refreshToken && user) {
|
|
1015
|
+
super.save(parsed.token, parsed.refreshToken, user);
|
|
933
1016
|
}
|
|
934
1017
|
} catch {
|
|
935
1018
|
}
|
|
936
1019
|
}
|
|
937
1020
|
/**
|
|
938
|
-
* Serialize
|
|
1021
|
+
* Serialize the session tokens into a `Set-Cookie` value. The `user` object is NOT included: its
|
|
1022
|
+
* `user_metadata` is already inside the access token, and sending it twice doubled the cookie
|
|
1023
|
+
* (5 KB of metadata gave 4 cookies and 13 139 bytes).
|
|
939
1024
|
*/
|
|
940
1025
|
exportToCookie(options = {}) {
|
|
941
1026
|
const data = {
|
|
942
1027
|
token: this.token,
|
|
943
|
-
refreshToken: this.refreshToken
|
|
944
|
-
user: this.user
|
|
1028
|
+
refreshToken: this.refreshToken
|
|
945
1029
|
};
|
|
946
1030
|
const encoded = encodeURIComponent(JSON.stringify(data));
|
|
947
1031
|
const parts = [`${this.key}=${encoded}`];
|
|
@@ -949,7 +1033,7 @@ var LocalAuthStore = class extends BaseAuthStore {
|
|
|
949
1033
|
if (options.domain) parts.push(`Domain=${options.domain}`);
|
|
950
1034
|
if (options.maxAge !== void 0) parts.push(`Max-Age=${options.maxAge}`);
|
|
951
1035
|
if (options.secure) parts.push("Secure");
|
|
952
|
-
if (options.sameSite) parts.push(`SameSite=${options.sameSite}`);
|
|
1036
|
+
if (options.sameSite) parts.push(`SameSite=${sameSiteAttribute(options.sameSite)}`);
|
|
953
1037
|
if (options.httpOnly) parts.push("HttpOnly");
|
|
954
1038
|
return parts.join("; ");
|
|
955
1039
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurabase/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
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",
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"author": "Aurabase",
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/
|
|
34
|
+
"url": "git+https://github.com/Aurabase-cloud/Aurabase.git",
|
|
35
35
|
"directory": "aurabase-js/packages/core"
|
|
36
36
|
},
|
|
37
37
|
"homepage": "https://aurabase.cloud",
|
|
38
38
|
"bugs": {
|
|
39
|
-
"url": "https://github.com/
|
|
39
|
+
"url": "https://github.com/Aurabase-cloud/Aurabase/issues"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"aurabase",
|