@aurabase/auth 0.6.2 → 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/dist/index.js CHANGED
@@ -54,6 +54,46 @@ var AuthAdminService = class extends BaseService {
54
54
  async generateLink(userId, type) {
55
55
  return this.request("POST", `${this.prefix}/users/${userId}/generate-link`, { type });
56
56
  }
57
+ /**
58
+ * Impersonation (AUTH-037) : jeton COURT (10 minutes au plus) pour agir en tant que l'utilisateur,
59
+ * avec le claim `act: { sub }`. Pas de refresh token, journalisé (`admin_impersonation`).
60
+ * Un compte `owner`/`admin` exige `allowPrivileged: true` ET une `reason`.
61
+ */
62
+ async impersonate(userId, options) {
63
+ return this.request("POST", `${this.prefix}/users/${userId}/impersonate`, {
64
+ reason: options?.reason,
65
+ ttl_seconds: options?.ttlSeconds,
66
+ allow_privileged: options?.allowPrivileged ?? false
67
+ });
68
+ }
69
+ /**
70
+ * Import en masse (AUTH-037) : jusqu'a 1000 utilisateurs par appel, avec hash bcrypt ou argon2id.
71
+ * Rapport ligne par ligne ; une ligne en erreur n'arrete pas les autres.
72
+ */
73
+ async importUsers(users) {
74
+ return this.request("POST", `${this.prefix}/users/import`, {
75
+ users: users.map((u) => ({
76
+ email: u.email,
77
+ email_verified: u.emailVerified ?? false,
78
+ password_hash: u.passwordHash,
79
+ name: u.name,
80
+ user_metadata: u.userMetadata,
81
+ app_metadata: u.appMetadata
82
+ }))
83
+ });
84
+ }
85
+ /** Cles de signature du projet (active + previous), URL du JWKS et emetteur (`iss`). */
86
+ async listSigningKeys() {
87
+ return this.request("GET", `${this.prefix}/signing-keys`);
88
+ }
89
+ /** Rotation : une nouvelle cle signe les nouveaux jetons, l'ancienne reste publiee 24 h. */
90
+ async rotateSigningKey() {
91
+ return this.request("POST", `${this.prefix}/signing-keys/rotate`, {});
92
+ }
93
+ /** Revoque une ancienne cle compromise (retiree du JWKS, jetons refuses). */
94
+ async revokeSigningKey(kid) {
95
+ return this.request("DELETE", `${this.prefix}/signing-keys/${encodeURIComponent(kid)}`);
96
+ }
57
97
  /**
58
98
  * Statistiques des utilisateurs : total, vérifiés, bannis, avec MFA et nouveaux sur 7 jours.
59
99
  */
@@ -351,6 +391,77 @@ async function codeChallengeS256(verifier) {
351
391
  return b64url(new Uint8Array(digest));
352
392
  }
353
393
 
394
+ // src/webauthn.ts
395
+ function base64urlToBytes(valeur) {
396
+ const b64 = valeur.replace(/-/g, "+").replace(/_/g, "/");
397
+ const bin = atob(b64 + "=".repeat((4 - b64.length % 4) % 4));
398
+ const octets = new Uint8Array(new ArrayBuffer(bin.length));
399
+ for (let i = 0; i < bin.length; i++) octets[i] = bin.charCodeAt(i);
400
+ return octets;
401
+ }
402
+ function bytesToBase64url(octets) {
403
+ const vue = octets instanceof ArrayBuffer ? new Uint8Array(octets) : new Uint8Array(octets.buffer, octets.byteOffset, octets.byteLength);
404
+ let bin = "";
405
+ for (const o of vue) bin += String.fromCharCode(o);
406
+ return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
407
+ }
408
+ function creationOptionsFromJson(json) {
409
+ const pk = json.publicKey;
410
+ return {
411
+ publicKey: {
412
+ ...pk,
413
+ challenge: base64urlToBytes(pk.challenge),
414
+ user: { ...pk.user, id: base64urlToBytes(pk.user.id) },
415
+ excludeCredentials: (pk.excludeCredentials ?? []).map((c) => ({
416
+ ...c,
417
+ id: base64urlToBytes(c.id)
418
+ }))
419
+ }
420
+ };
421
+ }
422
+ function requestOptionsFromJson(json) {
423
+ const pk = json.publicKey;
424
+ return {
425
+ publicKey: {
426
+ ...pk,
427
+ challenge: base64urlToBytes(pk.challenge),
428
+ allowCredentials: (pk.allowCredentials ?? []).map((c) => ({
429
+ ...c,
430
+ id: base64urlToBytes(c.id)
431
+ }))
432
+ }
433
+ };
434
+ }
435
+ function attestationToJson(cred) {
436
+ const r = cred.response;
437
+ return {
438
+ id: cred.id,
439
+ rawId: bytesToBase64url(cred.rawId),
440
+ type: cred.type,
441
+ response: {
442
+ attestationObject: bytesToBase64url(r.attestationObject),
443
+ clientDataJSON: bytesToBase64url(r.clientDataJSON),
444
+ transports: typeof r.getTransports === "function" ? r.getTransports() : void 0
445
+ },
446
+ extensions: cred.getClientExtensionResults?.() ?? {}
447
+ };
448
+ }
449
+ function assertionToJson(cred) {
450
+ const r = cred.response;
451
+ return {
452
+ id: cred.id,
453
+ rawId: bytesToBase64url(cred.rawId),
454
+ type: cred.type,
455
+ response: {
456
+ authenticatorData: bytesToBase64url(r.authenticatorData),
457
+ clientDataJSON: bytesToBase64url(r.clientDataJSON),
458
+ signature: bytesToBase64url(r.signature),
459
+ userHandle: r.userHandle ? bytesToBase64url(r.userHandle) : null
460
+ },
461
+ extensions: cred.getClientExtensionResults?.() ?? {}
462
+ };
463
+ }
464
+
354
465
  // src/SessionManager.ts
355
466
  var SessionManager = class {
356
467
  opts;
@@ -452,10 +563,16 @@ var AuthService = class extends BaseService {
452
563
  _stateListeners = /* @__PURE__ */ new Set();
453
564
  _sessionManager;
454
565
  _applyingRemote = false;
566
+ /** AUTH-011 — séquence des effacements de session et dernier effacement annoncé par SIGNED_OUT. */
567
+ _clearSeq = 0;
568
+ _signedOutSeq = 0;
455
569
  _unsubStore = null;
456
570
  _disposed = false;
571
+ /** AUTH-015 — flux OAuth par défaut du client. */
572
+ _defaultFlowType;
457
573
  constructor(client, options) {
458
574
  super(client);
575
+ this._defaultFlowType = options?.flowType;
459
576
  const browser = typeof window !== "undefined";
460
577
  const enabled = !!options?.forceEnabled || (options?.autoRefresh ?? true) && browser && (options?.persistSession ?? true);
461
578
  this._sessionManager = new SessionManager({
@@ -471,8 +588,18 @@ var AuthService = class extends BaseService {
471
588
  onRemoteMessage: (data) => this._applyRemoteState(data)
472
589
  });
473
590
  this._sessionManager.start();
591
+ let hadToken = !!this.client.authStore.token;
474
592
  this._unsubStore = this.client.authStore.onChange((token) => {
475
593
  this._sessionManager.setActive(!!token);
594
+ if (!token && hadToken && !this._applyingRemote) {
595
+ const seq = ++this._clearSeq;
596
+ queueMicrotask(() => {
597
+ if (this._signedOutSeq < seq && !this.client.authStore.token) {
598
+ this._emitEvent("SIGNED_OUT", null, null);
599
+ }
600
+ });
601
+ }
602
+ hadToken = !!token;
476
603
  if (!this._applyingRemote) {
477
604
  const state = this._broadcastState();
478
605
  if (!token || state !== null) {
@@ -687,8 +814,6 @@ var AuthService = class extends BaseService {
687
814
  const session = store.token && store.user ? {
688
815
  access_token: store.token,
689
816
  refresh_token: store.refreshToken ?? "",
690
- accessToken: store.token,
691
- refreshToken: store.refreshToken ?? "",
692
817
  token_type: "Bearer",
693
818
  expires_in: 900,
694
819
  user: store.user
@@ -699,11 +824,10 @@ var AuthService = class extends BaseService {
699
824
  return result;
700
825
  }
701
826
  _emitEvent(event, user, accessToken, extra) {
827
+ if (event === "SIGNED_OUT") this._signedOutSeq = this._clearSeq;
702
828
  const session = accessToken && user ? {
703
829
  access_token: accessToken,
704
830
  refresh_token: this.client.authStore.refreshToken ?? "",
705
- accessToken,
706
- refreshToken: this.client.authStore.refreshToken ?? "",
707
831
  token_type: "Bearer",
708
832
  expires_in: 900,
709
833
  user
@@ -734,15 +858,15 @@ var AuthService = class extends BaseService {
734
858
  if (req.options?.data) payload.data = req.options.data;
735
859
  if (req.options?.emailRedirectTo) payload.email_redirect_to = req.options.emailRedirectTo;
736
860
  const res = await this.request("POST", `${this.prefix}/register`, payload);
861
+ if (res.data && !isMfaChallenge(res.data) && !res.data.access_token) {
862
+ res.data.session = null;
863
+ return res;
864
+ }
737
865
  if (res.data && !isMfaChallenge(res.data)) {
738
866
  const tp = res.data;
739
- tp.accessToken = tp.access_token;
740
- tp.refreshToken = tp.refresh_token;
741
867
  tp.session = {
742
868
  access_token: tp.access_token,
743
869
  refresh_token: tp.refresh_token,
744
- accessToken: tp.access_token,
745
- refreshToken: tp.refresh_token,
746
870
  token_type: tp.token_type,
747
871
  expires_in: tp.expires_in,
748
872
  user: tp.user,
@@ -754,11 +878,6 @@ var AuthService = class extends BaseService {
754
878
  }
755
879
  if (res.data && isMfaChallenge(res.data)) {
756
880
  const mfa = res.data;
757
- mfa.mfaChallenge = {
758
- mfaRequired: mfa.mfa_required,
759
- mfaToken: mfa.mfa_token,
760
- mfaSetupRequired: mfa.mfa_setup_required
761
- };
762
881
  this._emitEvent("MFA_CHALLENGE", null, null, {
763
882
  mfaToken: mfa.mfa_token,
764
883
  mfaSetupRequired: mfa.mfa_setup_required
@@ -780,13 +899,9 @@ var AuthService = class extends BaseService {
780
899
  const res = await this.request("POST", `${this.prefix}/login`, payload);
781
900
  if (res.data && !isMfaChallenge(res.data)) {
782
901
  const tp = res.data;
783
- tp.accessToken = tp.access_token;
784
- tp.refreshToken = tp.refresh_token;
785
902
  tp.session = {
786
903
  access_token: tp.access_token,
787
904
  refresh_token: tp.refresh_token,
788
- accessToken: tp.access_token,
789
- refreshToken: tp.refresh_token,
790
905
  token_type: tp.token_type,
791
906
  expires_in: tp.expires_in,
792
907
  user: tp.user,
@@ -798,11 +913,6 @@ var AuthService = class extends BaseService {
798
913
  }
799
914
  if (res.data && isMfaChallenge(res.data)) {
800
915
  const mfa = res.data;
801
- mfa.mfaChallenge = {
802
- mfaRequired: mfa.mfa_required,
803
- mfaToken: mfa.mfa_token,
804
- mfaSetupRequired: mfa.mfa_setup_required
805
- };
806
916
  this._emitEvent("MFA_CHALLENGE", null, null, {
807
917
  mfaToken: mfa.mfa_token,
808
918
  mfaSetupRequired: mfa.mfa_setup_required
@@ -831,7 +941,7 @@ var AuthService = class extends BaseService {
831
941
  error: new AuraError(0, {
832
942
  error: {
833
943
  code: "invalid_provider",
834
- message: "signInWithOAuth: le fournisseur doit \xEAtre une cha\xEEne non vide, par exemple signInWithOAuth('google', { redirectTo }) ou signInWithOAuth({ provider: 'google', redirectTo })"
944
+ message: "signInWithOAuth: the provider must be a non-empty string, e.g. signInWithOAuth('google', { redirectTo }) or signInWithOAuth({ provider: 'google', redirectTo })"
835
945
  }
836
946
  })
837
947
  };
@@ -845,22 +955,22 @@ var AuthService = class extends BaseService {
845
955
  params.set(k, v);
846
956
  continue;
847
957
  }
848
- const raison = OAUTH_SDK_OWNED_QUERY_PARAMS.has(k) ? `il est construit par signInWithOAuth (utilisez l'option d\xE9di\xE9e : ${k === "redirect_uri" || k === "redirect_to" ? "redirectTo" : k === "scopes" ? "scopes" : k === "project_id" ? "l'URL du projet pass\xE9e \xE0 createClient()" : "flowType:'pkce'"})` : `il n'est pas transmis au provider. Seuls ces param\xE8tres d'exp\xE9rience de connexion le sont : ${[...OAUTH_FORWARDABLE_QUERY_PARAMS].join(", ")}`;
958
+ const raison = OAUTH_SDK_OWNED_QUERY_PARAMS.has(k) ? `it is built by signInWithOAuth (use the dedicated option: ${k === "redirect_uri" || k === "redirect_to" ? "redirectTo" : k === "scopes" ? "scopes" : k === "project_id" ? "the project URL passed to createClient()" : "flowType:'pkce'"})` : `it is not forwarded to the provider. Only these sign-in experience parameters are: ${[...OAUTH_FORWARDABLE_QUERY_PARAMS].join(", ")}`;
849
959
  return {
850
960
  data: null,
851
961
  error: new AuraError(400, {
852
962
  error: {
853
963
  code: "invalid_oauth_query_param",
854
- message: `signInWithOAuth: queryParams.${k} refus\xE9 \u2014 ${raison}.`
964
+ message: `signInWithOAuth: queryParams.${k} rejected \u2014 ${raison}.`
855
965
  }
856
966
  })
857
967
  };
858
968
  }
859
969
  }
860
- if (options?.flowType === "pkce") {
970
+ if ((options?.flowType ?? this._defaultFlowType) === "pkce") {
861
971
  if (typeof window !== "undefined" && !this.client.authStore.persistent) {
862
972
  console.warn(
863
- `[aurabase] PKCE (flowType:"pkce") requiert un store persistant (LocalAuthStore) pour que le code_verifier survive au redirect OAuth ; le store courant est en m\xE9moire \u2014 l'\xE9change \xE9chouera au retour.`
973
+ '[aurabase] PKCE (flowType:"pkce") requires a persistent store (LocalAuthStore) so that the code_verifier survives the OAuth redirect; the current store is in-memory, so the exchange will fail on return.'
864
974
  );
865
975
  }
866
976
  try {
@@ -875,7 +985,7 @@ var AuthService = class extends BaseService {
875
985
  error: new AuraError(0, {
876
986
  error: {
877
987
  code: "pkce_generation_failed",
878
- message: "PKCE: g\xE9n\xE9ration du code_verifier/challenge impossible (WebCrypto indisponible)"
988
+ message: "PKCE: cannot generate the code_verifier/challenge (WebCrypto unavailable)"
879
989
  }
880
990
  })
881
991
  };
@@ -883,7 +993,7 @@ var AuthService = class extends BaseService {
883
993
  } else {
884
994
  if (typeof window !== "undefined" && !this.client.authStore.persistent) {
885
995
  console.warn(
886
- '[aurabase] S\xC9CURIT\xC9 : signInWithOAuth en flux implicit avec un store NON persistant (m\xE9moire) \u2014 le marqueur anti session-fixation ne survivra pas au redirect OAuth et la session ne sera pas adopt\xE9e au retour. Utilisez un LocalAuthStore, et pr\xE9f\xE9rez flowType:"pkce" (recommand\xE9, immunis\xE9 contre la session fixation).'
996
+ '[aurabase] SECURITY: signInWithOAuth in implicit flow with a NON-persistent store (in-memory): the anti session-fixation marker will not survive the OAuth redirect and the session will not be adopted on return. Use a LocalAuthStore, and prefer flowType:"pkce" (recommended, immune to session fixation).'
887
997
  );
888
998
  }
889
999
  try {
@@ -892,7 +1002,7 @@ var AuthService = class extends BaseService {
892
1002
  }
893
1003
  }
894
1004
  const url = `${this.client.baseUrl}/v1/auth/oauth/${encodeURIComponent(provider)}/start?${params.toString()}`;
895
- if (typeof window !== "undefined") {
1005
+ if (typeof window !== "undefined" && window.location) {
896
1006
  window.location.href = url;
897
1007
  }
898
1008
  return { data: { url }, error: null };
@@ -949,11 +1059,9 @@ var AuthService = class extends BaseService {
949
1059
  return res;
950
1060
  }
951
1061
  /**
952
- * Session courante lue depuis le store (synchrone en pratique, pas d'appel réseau).
953
- *
954
- * DOUBLE COMPATIBILITÉ DX (Parité Supabase & compatibilité existante Aurabase) :
955
- * - Permet `const { data: { session }, error } = await aura.auth.getSession()` (forme canonique)
956
- * - Permet `const { accessToken, user } = await aura.auth.getSession()` (forme plate historique)
1062
+ * Session courante lue depuis le store (pas d'appel réseau). Forme unique (AUTH-019) :
1063
+ * `const { data: { session } } = await aura.auth.getSession()`.
1064
+ * Côté serveur, cette session n'est PAS vérifiée : autorisez avec `getUser()` (AUTH-004).
957
1065
  */
958
1066
  getSession() {
959
1067
  const token = this.client.authStore.token;
@@ -961,19 +1069,11 @@ var AuthService = class extends BaseService {
961
1069
  const session = token && user ? {
962
1070
  access_token: token,
963
1071
  refresh_token: this.client.authStore.refreshToken ?? "",
964
- accessToken: token,
965
- refreshToken: this.client.authStore.refreshToken ?? "",
966
1072
  token_type: "Bearer",
967
1073
  expires_in: 900,
968
1074
  user
969
1075
  } : null;
970
- return Promise.resolve({
971
- data: { session },
972
- error: null,
973
- accessToken: token,
974
- access_token: token,
975
- user
976
- });
1076
+ return Promise.resolve({ data: { session }, error: null });
977
1077
  }
978
1078
  /**
979
1079
  * Installe une session à partir d'un jeton d'accès et d'un jeton de rafraîchissement existants (par exemple reçus d'un serveur).
@@ -1241,7 +1341,7 @@ var AuthService = class extends BaseService {
1241
1341
  error: new AuraError(0, {
1242
1342
  error: {
1243
1343
  code: "allow_expired_unsupported_on_fallback",
1244
- message: "getClaims: allowExpired ne peut pas \xEAtre honor\xE9 sur le chemin de validation serveur (fallback HS256/JWKS)"
1344
+ message: "getClaims: allowExpired cannot be honored on the server validation path (HS256/JWKS fallback)"
1245
1345
  }
1246
1346
  })
1247
1347
  };
@@ -1269,7 +1369,7 @@ var AuthService = class extends BaseService {
1269
1369
  error: new AuraError(0, {
1270
1370
  error: {
1271
1371
  code: "invalid_response",
1272
- message: "getClaims: la validation serveur du token (getUser) a r\xE9pondu sans contenu \u2014 claims ind\xE9terminables"
1372
+ message: "getClaims: the server token validation (getUser) answered with no content, so the claims cannot be determined"
1273
1373
  }
1274
1374
  })
1275
1375
  };
@@ -1285,6 +1385,17 @@ var AuthService = class extends BaseService {
1285
1385
  * Supprime le compte de l'utilisateur connecté après confirmation par son mot de passe, puis vide la session locale.
1286
1386
  */
1287
1387
  async deleteUser(password) {
1388
+ if (!password || typeof password !== "string" || password.trim() === "") {
1389
+ return {
1390
+ data: null,
1391
+ error: new AuraError(400, {
1392
+ error: {
1393
+ code: "PASSWORD_REQUIRED",
1394
+ message: "Password is required to delete account"
1395
+ }
1396
+ })
1397
+ };
1398
+ }
1288
1399
  const res = await this.request("DELETE", `${this.prefix}/user`, { password });
1289
1400
  if (!res.error) {
1290
1401
  this.client.authStore.clear();
@@ -1346,9 +1457,107 @@ var AuthService = class extends BaseService {
1346
1457
  regenerateRecoveryCodes: (codeOrParams) => {
1347
1458
  const code = typeof codeOrParams === "string" ? codeOrParams : codeOrParams.code;
1348
1459
  return this.regenerateRecoveryCodes(code);
1460
+ },
1461
+ factors: (mfaToken) => this.request("POST", `${this.prefix}/mfa/factors`, { mfa_token: mfaToken }),
1462
+ sms: {
1463
+ setup: (phone) => this.request("POST", `${this.prefix}/mfa/sms/setup`, { phone }),
1464
+ verifySetup: (code) => this.request("POST", `${this.prefix}/mfa/sms/setup/verify`, { code }),
1465
+ send: (mfaToken) => this.request("POST", `${this.prefix}/mfa/sms/send`, {
1466
+ mfa_token: mfaToken
1467
+ }),
1468
+ remove: (password) => this.request("DELETE", `${this.prefix}/mfa/sms`, { password })
1469
+ },
1470
+ webauthn: {
1471
+ register: (params) => this.webauthnRegister(params),
1472
+ verify: (params) => this.webauthnVerify(params),
1473
+ list: () => this.request("GET", `${this.prefix}/mfa/webauthn`),
1474
+ remove: (id, password) => this.request(
1475
+ "DELETE",
1476
+ `${this.prefix}/mfa/webauthn/${encodeURIComponent(id)}`,
1477
+ { password }
1478
+ )
1349
1479
  }
1350
1480
  };
1351
1481
  }
1482
+ webauthnIndisponible() {
1483
+ return {
1484
+ data: null,
1485
+ error: new AuraError(0, {
1486
+ error: {
1487
+ code: "webauthn_unavailable",
1488
+ message: "WebAuthn is not available here: navigator.credentials is missing. Use a browser in a secure context (https or localhost), or pass `credentials` explicitly."
1489
+ }
1490
+ })
1491
+ };
1492
+ }
1493
+ /**
1494
+ * Enrôle une passkey WebAuthn pour l'utilisateur connecté (cérémonie complète : options du serveur,
1495
+ * `navigator.credentials.create`, confirmation). Le premier facteur du compte active le MFA et
1496
+ * renvoie les codes de récupération (`recovery_codes`) — à afficher une seule fois.
1497
+ */
1498
+ async webauthnRegister(params) {
1499
+ const conteneur = params?.credentials ?? (typeof navigator !== "undefined" ? navigator.credentials : void 0);
1500
+ if (!conteneur) return this.webauthnIndisponible();
1501
+ const debut = await this.request(
1502
+ "POST",
1503
+ `${this.prefix}/mfa/webauthn/register/start`,
1504
+ { friendly_name: params?.friendlyName }
1505
+ );
1506
+ if (!debut.data) return debut;
1507
+ let cred;
1508
+ try {
1509
+ cred = await conteneur.create(creationOptionsFromJson(debut.data.options));
1510
+ } catch (e) {
1511
+ return this.webauthnAnnule(e);
1512
+ }
1513
+ if (!cred) return this.webauthnAnnule(new Error("no credential returned"));
1514
+ return this.request("POST", `${this.prefix}/mfa/webauthn/register/finish`, {
1515
+ credential: attestationToJson(cred),
1516
+ friendly_name: params?.friendlyName
1517
+ });
1518
+ }
1519
+ /**
1520
+ * Relève un défi MFA (`mfaToken`, reçu d'un `signIn`) avec une passkey : options du serveur,
1521
+ * `navigator.credentials.get`, vérification. Enregistre la session et émet `SIGNED_IN`.
1522
+ */
1523
+ async webauthnVerify(params) {
1524
+ const conteneur = params.credentials ?? (typeof navigator !== "undefined" ? navigator.credentials : void 0);
1525
+ if (!conteneur) return this.webauthnIndisponible();
1526
+ const debut = await this.request(
1527
+ "POST",
1528
+ `${this.prefix}/mfa/webauthn/verify/start`,
1529
+ { mfa_token: params.mfaToken }
1530
+ );
1531
+ if (!debut.data) return debut;
1532
+ let cred;
1533
+ try {
1534
+ cred = await conteneur.get(requestOptionsFromJson(debut.data.options));
1535
+ } catch (e) {
1536
+ return this.webauthnAnnule(e);
1537
+ }
1538
+ if (!cred) return this.webauthnAnnule(new Error("no credential returned"));
1539
+ const res = await this.request("POST", `${this.prefix}/mfa/webauthn/verify/finish`, {
1540
+ mfa_token: params.mfaToken,
1541
+ credential: assertionToJson(cred)
1542
+ });
1543
+ if (res.data) {
1544
+ this.client.authStore.save(res.data.access_token, res.data.refresh_token, res.data.user);
1545
+ this._emitEvent("SIGNED_IN", res.data.user, res.data.access_token);
1546
+ }
1547
+ return res;
1548
+ }
1549
+ webauthnAnnule(cause) {
1550
+ const nom = cause instanceof Error ? cause.name : "Error";
1551
+ return {
1552
+ data: null,
1553
+ error: new AuraError(0, {
1554
+ error: {
1555
+ code: nom === "NotAllowedError" ? "webauthn_cancelled" : "webauthn_failed",
1556
+ message: nom === "NotAllowedError" ? "The passkey prompt was cancelled or timed out." : `The passkey ceremony failed: ${cause instanceof Error ? cause.message : String(cause)}`
1557
+ }
1558
+ })
1559
+ };
1560
+ }
1352
1561
  /** MFA : démarre l'enrôlement TOTP et renvoie le `secret` et l'URL `otpauth_url` à afficher en QR code. */
1353
1562
  async mfaEnroll() {
1354
1563
  return this.request("POST", `${this.prefix}/mfa/setup`);
@@ -1518,10 +1727,10 @@ var AuthService = class extends BaseService {
1518
1727
  async getSettings() {
1519
1728
  return this.request("GET", `${this.prefix}/settings`);
1520
1729
  }
1521
- /** Clé publiable (anon key) du projet — GET /anon-key (public, à l'image du modèle Supabase). */
1522
- async getAnonKey() {
1523
- return this.request("GET", `${this.prefix}/anon-key`);
1524
- }
1730
+ // AUTH-023 — `getAnonKey()` retirée : la route `/anon-key` n'est servie qu'aux projets qui
1731
+ // activent l'auth hébergée (garde C1, aucun BaaS n'expose la clé d'un projet à un anonyme), et
1732
+ // un client qui l'appelle possède déjà la clé anon (passée à createClient). La méthode
1733
+ // répondait donc 404 pour presque tous les projets.
1525
1734
  // -- Password recovery (token consumption) ----------------------------------
1526
1735
  /**
1527
1736
  * Consomme le token de récupération de mot de passe et applique le nouveau mot de passe.
@@ -1558,4 +1767,4 @@ var AuthService = class extends BaseService {
1558
1767
  }
1559
1768
  };
1560
1769
 
1561
- export { AuthAdminService, AuthService, SessionManager, isMfaChallenge };
1770
+ export { AuthAdminService, AuthService, SessionManager, assertionToJson, attestationToJson, base64urlToBytes, bytesToBase64url, creationOptionsFromJson, isMfaChallenge, requestOptionsFromJson };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurabase/auth",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Service d'authentification du SDK Aurabase (sessions, OAuth/PKCE, MFA, magic link, OTP, admin)",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -46,7 +46,7 @@
46
46
  "authentication"
47
47
  ],
48
48
  "dependencies": {
49
- "@aurabase/core": "0.6.2"
49
+ "@aurabase/core": "0.7.0"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "20.14.15",