@aurabase/auth 0.6.0 → 0.6.2

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 CHANGED
@@ -279,7 +279,7 @@ Réservé au serveur (clé `service_role`). Deux façons d'y accéder :
279
279
  // Via le client admin (recommandé)
280
280
  import { createAdminClient } from '@aurabase/aurabase-js'
281
281
 
282
- const admin = createAdminClient('https://mon-projet.aurabase.cloud', process.env.SERVICE_ROLE_KEY!)
282
+ const admin = createAdminClient('https://mon-projet.aurabase.cloud', process.env.AURABASE_SERVICE_ROLE_KEY!)
283
283
  await admin.authAdmin.listUsers({ limit: 50 })
284
284
  ```
285
285
 
package/dist/index.cjs CHANGED
@@ -13,6 +13,9 @@ var AuthAdminService = class extends core.BaseService {
13
13
  return "/v1/auth/admin";
14
14
  }
15
15
  // -- Users ----------------------------------------------------------------
16
+ /**
17
+ * Liste les utilisateurs du projet, avec recherche (`search`) et pagination (`limit`, `offset`).
18
+ */
16
19
  async listUsers(params) {
17
20
  const query = new URLSearchParams();
18
21
  if (params?.limit !== void 0) query.set("limit", String(params.limit));
@@ -21,34 +24,52 @@ var AuthAdminService = class extends core.BaseService {
21
24
  const qs = query.toString();
22
25
  return this.request("GET", `${this.prefix}/users${qs ? "?" + qs : ""}`);
23
26
  }
27
+ /** Lit un utilisateur par son identifiant. */
24
28
  async getUser(userId) {
25
29
  return this.request("GET", `${this.prefix}/users/${userId}`);
26
30
  }
31
+ /**
32
+ * Crée un utilisateur sans passer par l'inscription publique (e-mail, mot de passe, métadonnées, rôle).
33
+ */
27
34
  async createUser(data) {
28
35
  return this.request("POST", `${this.prefix}/users`, data);
29
36
  }
37
+ /** Met à jour un utilisateur : seuls les champs fournis changent. */
30
38
  async updateUser(userId, data) {
31
39
  return this.request("PUT", `${this.prefix}/users/${userId}`, data);
32
40
  }
41
+ /** Supprime un utilisateur. */
33
42
  async deleteUser(userId) {
34
43
  return this.request("DELETE", `${this.prefix}/users/${userId}`);
35
44
  }
45
+ /** Bannit un utilisateur : il ne peut plus se connecter. */
36
46
  async banUser(userId) {
37
47
  return this.request("POST", `${this.prefix}/users/${userId}/ban`, {});
38
48
  }
49
+ /** Lève le bannissement d'un utilisateur. */
39
50
  async unbanUser(userId) {
40
51
  return this.request("POST", `${this.prefix}/users/${userId}/unban`, {});
41
52
  }
53
+ /**
54
+ * Génère un lien à usage unique pour un utilisateur (`'magic'`, `'recovery'` ou `'invite'`) et renvoie `{ link, token }`.
55
+ */
42
56
  async generateLink(userId, type) {
43
57
  return this.request("POST", `${this.prefix}/users/${userId}/generate-link`, { type });
44
58
  }
59
+ /**
60
+ * Statistiques des utilisateurs : total, vérifiés, bannis, avec MFA et nouveaux sur 7 jours.
61
+ */
45
62
  async getUserStats() {
46
63
  return this.request("GET", `${this.prefix}/users/stats`);
47
64
  }
65
+ /** Liste les identités liées à un utilisateur (fournisseurs OAuth, e-mail). */
48
66
  async listUserIdentities(userId) {
49
67
  return this.request("GET", `${this.prefix}/users/${userId}/identities`);
50
68
  }
51
69
  // -- Sessions ---------------------------------------------------------------
70
+ /**
71
+ * Liste les sessions actives de tous les utilisateurs, filtrables par `userId`, avec pagination.
72
+ */
52
73
  async listSessions(params) {
53
74
  const query = new URLSearchParams();
54
75
  if (params?.userId !== void 0) query.set("user_id", params.userId);
@@ -57,10 +78,14 @@ var AuthAdminService = class extends core.BaseService {
57
78
  const qs = query.toString();
58
79
  return this.request("GET", `${this.prefix}/sessions${qs ? "?" + qs : ""}`);
59
80
  }
81
+ /** Révoque une session par son identifiant. */
60
82
  async revokeSession(sessionId) {
61
83
  return this.request("DELETE", `${this.prefix}/sessions/${sessionId}`);
62
84
  }
63
85
  // -- Audit log ------------------------------------------------------------
86
+ /**
87
+ * Lit le journal d'audit (action, adresse IP, agent utilisateur, date), filtrable par `userId`, avec pagination.
88
+ */
64
89
  async getAuditLog(params) {
65
90
  const query = new URLSearchParams();
66
91
  if (params?.userId !== void 0) query.set("user_id", params.userId);
@@ -70,6 +95,9 @@ var AuthAdminService = class extends core.BaseService {
70
95
  return this.request("GET", `${this.prefix}/audit-log${qs ? "?" + qs : ""}`);
71
96
  }
72
97
  // -- Settings -------------------------------------------------------------
98
+ /**
99
+ * Lit les réglages d'authentification du projet. Les secrets (mot de passe SMTP, clés) ne sont jamais renvoyés.
100
+ */
73
101
  async getSettings() {
74
102
  return this.request("GET", `${this.prefix}/settings`);
75
103
  }
@@ -92,18 +120,23 @@ var AuthAdminService = class extends core.BaseService {
92
120
  return this.request("PUT", `${this.prefix}/settings`, data);
93
121
  }
94
122
  // -- Email templates ------------------------------------------------------
123
+ /** Liste les modèles d'e-mail d'authentification du projet. */
95
124
  async listEmailTemplates() {
96
125
  return this.request("GET", `${this.prefix}/email-templates`);
97
126
  }
127
+ /** Lit le modèle d'e-mail d'un type donné (par exemple confirmation, récupération). */
98
128
  async getEmailTemplate(type) {
99
129
  return this.request("GET", `${this.prefix}/email-templates/${type}`);
100
130
  }
131
+ /** Remplace le contenu du modèle d'e-mail d'un type donné. */
101
132
  async updateEmailTemplate(type, data) {
102
133
  return this.request("PUT", `${this.prefix}/email-templates/${type}`, data);
103
134
  }
135
+ /** Supprime le modèle personnalisé d'un type d'e-mail. */
104
136
  async deleteEmailTemplate(type) {
105
137
  return this.request("DELETE", `${this.prefix}/email-templates/${type}`);
106
138
  }
139
+ /** Envoie un e-mail de test avec le modèle d'un type à l'adresse `to_email`. */
107
140
  async testEmailTemplate(type, body) {
108
141
  return this.request("POST", `${this.prefix}/email-templates/${type}/test`, body);
109
142
  }
@@ -632,6 +665,10 @@ var AuthService = class extends core.BaseService {
632
665
  return `aura-${core.clientScope(this.client.baseUrl)}-oauth-implicit-initiated`;
633
666
  }
634
667
  // -- Auth state change ------------------------------------------------------
668
+ /**
669
+ * Écoute les changements d'état de la session (`SIGNED_IN`, `SIGNED_OUT`, `TOKEN_REFRESHED`, `USER_UPDATED`,
670
+ * `MFA_CHALLENGE`…). Renvoie de quoi se désabonner : `unsubscribe()` ou `data.subscription.unsubscribe()`.
671
+ */
635
672
  onAuthStateChange(callback) {
636
673
  const unsub = () => {
637
674
  this._stateListeners.delete(callback);
@@ -681,8 +718,24 @@ var AuthService = class extends core.BaseService {
681
718
  }
682
719
  }
683
720
  // -- Sign up / sign in / sign out -------------------------------------------
721
+ /**
722
+ * Crée un compte avec e-mail et mot de passe. Si le projet n'exige pas de confirmation et que le MFA n'est pas actif,
723
+ * la session est enregistrée et `SIGNED_IN` est émis ; sinon la réponse est un défi MFA (voir `isMfaChallenge`).
724
+ */
684
725
  async signUp(req) {
685
- const res = await this.request("POST", `${this.prefix}/register`, req);
726
+ const payload = {
727
+ email: req.email,
728
+ password: req.password
729
+ };
730
+ if (req.captcha_token) payload.captcha_token = req.captcha_token;
731
+ if (req.name) {
732
+ payload.name = req.name;
733
+ } else if (req.options?.data && typeof req.options.data.name === "string") {
734
+ payload.name = req.options.data.name;
735
+ }
736
+ if (req.options?.data) payload.data = req.options.data;
737
+ if (req.options?.emailRedirectTo) payload.email_redirect_to = req.options.emailRedirectTo;
738
+ const res = await this.request("POST", `${this.prefix}/register`, payload);
686
739
  if (res.data && !isMfaChallenge(res.data)) {
687
740
  const tp = res.data;
688
741
  tp.accessToken = tp.access_token;
@@ -715,8 +768,18 @@ var AuthService = class extends core.BaseService {
715
768
  }
716
769
  return res;
717
770
  }
771
+ /**
772
+ * Connecte un utilisateur par e-mail et mot de passe. Enregistre la session et émet `SIGNED_IN` ;
773
+ * si le MFA est actif, renvoie un défi (`isMfaChallenge`) à compléter par `mfaVerify`.
774
+ */
718
775
  async signInWithPassword(req) {
719
- const res = await this.request("POST", `${this.prefix}/login`, req);
776
+ const payload = {
777
+ email: req.email,
778
+ password: req.password
779
+ };
780
+ if (req.captcha_token) payload.captcha_token = req.captcha_token;
781
+ if (req.mfa) payload.mfa = req.mfa;
782
+ const res = await this.request("POST", `${this.prefix}/login`, payload);
720
783
  if (res.data && !isMfaChallenge(res.data)) {
721
784
  const tp = res.data;
722
785
  tp.accessToken = tp.access_token;
@@ -749,37 +812,32 @@ var AuthService = class extends core.BaseService {
749
812
  }
750
813
  return res;
751
814
  }
752
- /**
753
- * Démarre un flux OAuth : renvoie toujours `{ url }` (et redirige en plus le navigateur
754
- * si un `window` existe).
755
- *
756
- * ### `options.queryParams` — contrat (N22)
757
- *
758
- * Les clés listées par {@link OAUTH_FORWARDABLE_QUERY_PARAMS} — `prompt`, `login_hint`,
759
- * `display`, `ui_locales`, `max_age`, `acr_values`, `claims_locales`, `access_type`,
760
- * `include_granted_scopes`, `hd` — sont **réellement transmises au provider**, jusque
761
- * dans l'URL d'autorisation.
762
- *
763
- * Toute autre clé est **refusée** : la méthode renvoie
764
- * `error.code === 'invalid_oauth_query_param'` et n'amorce aucune redirection. Rien
765
- * n'est jamais accepté puis jeté en silence. Sont notamment refusés :
766
- * - les paramètres du protocole (`state`, `nonce`, `client_id`, `response_type`,
767
- * `scope`, `code_challenge`…) — ils sont calculés par le serveur ;
768
- * - les paramètres construits par cette méthode (`redirect_uri`, `scopes`) — passez par
769
- * `redirectTo`, `scopes` ou `flowType` ;
770
- * - `project_id` : le projet est porté par l'URL du client, jamais par un paramètre ;
771
- * - les variantes de casse (`Prompt`, `State`) et les clés inconnues.
772
- *
773
- * L'autorité est le **backend** (`OAUTH_FORWARDABLE_PARAMS` de
774
- * `services/aura-auth/src/handlers/oauth.rs`), qui répond `400` sur les mêmes règles :
775
- * l'endpoint de démarrage est public et appelable sans SDK. Ce filtre-ci n'est que son
776
- * miroir, pour échouer avant la redirection ; un test de parité interdit qu'ils divergent.
777
- *
778
- * Les **valeurs**, elles, ne sont validées que par le backend (non vide, sans caractère
779
- * de contrôle, 512 caractères au plus, jamais deux fois la même clé) — un manquement
780
- * donne un `400` au démarrage du flux, pas une erreur de cette méthode.
781
- */
782
- async signInWithOAuth(provider, options) {
815
+ async signInWithOAuth(providerOrOptions, rawOptions) {
816
+ let provider;
817
+ let options;
818
+ if (typeof providerOrOptions === "object" && providerOrOptions !== null) {
819
+ provider = providerOrOptions.provider;
820
+ const nestedOptions = providerOrOptions.options;
821
+ options = {
822
+ ...providerOrOptions,
823
+ ...nestedOptions,
824
+ ...rawOptions
825
+ };
826
+ } else {
827
+ provider = providerOrOptions;
828
+ options = rawOptions;
829
+ }
830
+ if (typeof provider !== "string" || provider.trim() === "") {
831
+ return {
832
+ data: null,
833
+ error: new core.AuraError(0, {
834
+ error: {
835
+ code: "invalid_provider",
836
+ message: "signInWithOAuth: le fournisseur doit \xEAtre une cha\xEEne non vide, par exemple signInWithOAuth('google', { redirectTo }) ou signInWithOAuth({ provider: 'google', redirectTo })"
837
+ }
838
+ })
839
+ };
840
+ }
783
841
  const params = new URLSearchParams();
784
842
  if (options?.redirectTo) params.set("redirect_uri", options.redirectTo);
785
843
  if (options?.scopes) params.set("scopes", options.scopes);
@@ -841,9 +899,15 @@ var AuthService = class extends core.BaseService {
841
899
  }
842
900
  return { data: { url }, error: null };
843
901
  }
902
+ /**
903
+ * Envoie un lien de connexion par e-mail (« magic link »). La session s'obtient ensuite par `verifyOtp({ type: 'magiclink' })`.
904
+ */
844
905
  async signInWithMagicLink(req) {
845
906
  return this.request("POST", `${this.prefix}/magic-link`, req);
846
907
  }
908
+ /**
909
+ * Crée une session anonyme (utilisateur sans e-mail), enregistrée et signalée par `SIGNED_IN`.
910
+ */
847
911
  async signInAnonymously() {
848
912
  const res = await this.request("POST", `${this.prefix}/anonymous`);
849
913
  if (res.data) {
@@ -852,6 +916,9 @@ var AuthService = class extends core.BaseService {
852
916
  }
853
917
  return res;
854
918
  }
919
+ /**
920
+ * Déconnecte : révoque le jeton de rafraîchissement côté serveur s'il existe, vide la session locale et émet `SIGNED_OUT`.
921
+ */
855
922
  async signOut() {
856
923
  const refreshToken = this.client.authStore.refreshToken;
857
924
  if (!refreshToken) {
@@ -872,6 +939,10 @@ var AuthService = class extends core.BaseService {
872
939
  return res;
873
940
  }
874
941
  // -- Session management -----------------------------------------------------
942
+ /**
943
+ * Rafraîchit la session avec le jeton de rafraîchissement et émet `TOKEN_REFRESHED`.
944
+ * Les rafraîchissements concurrents sont fusionnés en un seul appel.
945
+ */
875
946
  async refreshSession() {
876
947
  const res = await core.refreshOnce(this.client);
877
948
  if (res.data) {
@@ -906,6 +977,10 @@ var AuthService = class extends core.BaseService {
906
977
  user
907
978
  });
908
979
  }
980
+ /**
981
+ * 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).
982
+ * Un jeton d'accès expiré est rafraîchi ; une session invalide est refusée et l'état précédent est restauré.
983
+ */
909
984
  async setSession(accessToken, refreshToken) {
910
985
  const store = this.client.authStore;
911
986
  const prev = { token: store.token, refreshToken: store.refreshToken, user: store.user };
@@ -992,6 +1067,9 @@ var AuthService = class extends core.BaseService {
992
1067
  this.client.authStore.removeItem(verifierKey);
993
1068
  }
994
1069
  }
1070
+ /**
1071
+ * Liste les sessions de l'utilisateur connecté (appareil, adresse IP, dates) ; `is_current` repère la session courante.
1072
+ */
995
1073
  async listSessions() {
996
1074
  const store = this.client.authStore;
997
1075
  if (store.token && store.refreshToken && !store.isValid) {
@@ -1005,9 +1083,13 @@ var AuthService = class extends core.BaseService {
1005
1083
  headers: refreshToken ? { "x-refresh-token": refreshToken } : void 0
1006
1084
  });
1007
1085
  }
1086
+ /** Révoque une session de l'utilisateur connecté par son identifiant. */
1008
1087
  async revokeSession(sessionId) {
1009
1088
  return this.request("DELETE", `${this.prefix}/sessions/${sessionId}`);
1010
1089
  }
1090
+ /**
1091
+ * Déconnecte l'utilisateur partout : révoque toutes ses sessions, vide la session locale et émet `SIGNED_OUT`.
1092
+ */
1011
1093
  async revokeAllSessions() {
1012
1094
  const rt = this.client.authStore.refreshToken;
1013
1095
  if (!rt) {
@@ -1043,13 +1125,26 @@ var AuthService = class extends core.BaseService {
1043
1125
  * applique l'isolation projet native : un token d'un AUTRE projet → 403.
1044
1126
  */
1045
1127
  async getUser(jwt) {
1128
+ let res;
1046
1129
  if (jwt !== void 0) {
1047
- return this.request("GET", `${this.prefix}/user`, void 0, {
1130
+ res = await this.request("GET", `${this.prefix}/user`, void 0, {
1048
1131
  authToken: jwt,
1049
1132
  skipAuthRefresh: true
1050
1133
  });
1134
+ } else {
1135
+ res = await this.request("GET", `${this.prefix}/user`);
1136
+ }
1137
+ if (res.data && typeof res.data === "object") {
1138
+ const userObj = res.data;
1139
+ Object.defineProperty(userObj, "user", {
1140
+ get() {
1141
+ return userObj;
1142
+ },
1143
+ enumerable: false,
1144
+ configurable: true
1145
+ });
1051
1146
  }
1052
- return this.request("GET", `${this.prefix}/user`);
1147
+ return res;
1053
1148
  }
1054
1149
  /**
1055
1150
  * Vérifie et décode les claims d'un JWT.
@@ -1188,6 +1283,9 @@ var AuthService = class extends core.BaseService {
1188
1283
  error: new core.AuraError(0, { error: { code: r.code, message: `getClaims: ${r.code}` } })
1189
1284
  };
1190
1285
  }
1286
+ /**
1287
+ * Supprime le compte de l'utilisateur connecté après confirmation par son mot de passe, puis vide la session locale.
1288
+ */
1191
1289
  async deleteUser(password) {
1192
1290
  const res = await this.request("DELETE", `${this.prefix}/user`, { password });
1193
1291
  if (!res.error) {
@@ -1196,6 +1294,9 @@ var AuthService = class extends core.BaseService {
1196
1294
  }
1197
1295
  return res;
1198
1296
  }
1297
+ /**
1298
+ * Met à jour l'utilisateur connecté (métadonnées, mot de passe…) et émet `USER_UPDATED`. Un changement d'e-mail ne prend effet qu'après confirmation par `verifyOtp({ type: 'email_change' })`.
1299
+ */
1199
1300
  async updateUser(req) {
1200
1301
  const res = await this.request("PUT", `${this.prefix}/user`, req);
1201
1302
  if (res.data) {
@@ -1208,9 +1309,14 @@ var AuthService = class extends core.BaseService {
1208
1309
  return res;
1209
1310
  }
1210
1311
  // -- Password recovery ------------------------------------------------------
1211
- async resetPasswordForEmail(req) {
1212
- return this.request("POST", `${this.prefix}/forgot-password`, req);
1312
+ /** Envoie un e-mail de réinitialisation du mot de passe (accepte un email sous forme de chaîne ou un objet PasswordResetRequest). */
1313
+ async resetPasswordForEmail(emailOrReq) {
1314
+ const body = typeof emailOrReq === "string" ? { email: emailOrReq } : emailOrReq;
1315
+ return this.request("POST", `${this.prefix}/forgot-password`, body);
1213
1316
  }
1317
+ /**
1318
+ * Change le mot de passe de l'utilisateur connecté. Le serveur révoque toutes ses sessions : le SDK vide la session locale et émet `SIGNED_OUT`.
1319
+ */
1214
1320
  async updatePassword(newPassword, currentPassword) {
1215
1321
  const res = await this.request("PUT", `${this.prefix}/user`, {
1216
1322
  password: newPassword,
@@ -1223,12 +1329,39 @@ var AuthService = class extends core.BaseService {
1223
1329
  return res;
1224
1330
  }
1225
1331
  // -- MFA --------------------------------------------------------------------
1332
+ /**
1333
+ * Façade unifiée pour les opérations MFA (TOTP).
1334
+ * Permet d'appeler indifféremment `aura.auth.mfa.enroll()` ou `aura.auth.mfaEnroll()`.
1335
+ */
1336
+ get mfa() {
1337
+ return {
1338
+ enroll: () => this.mfaEnroll(),
1339
+ verifySetup: (codeOrParams) => {
1340
+ const code = typeof codeOrParams === "string" ? codeOrParams : codeOrParams.code;
1341
+ return this.mfaVerifySetup(code);
1342
+ },
1343
+ verify: (req) => this.mfaVerify(req),
1344
+ unenroll: (passwordOrParams) => {
1345
+ const password = typeof passwordOrParams === "string" ? passwordOrParams : passwordOrParams.password;
1346
+ return this.mfaUnenroll(password);
1347
+ },
1348
+ regenerateRecoveryCodes: (codeOrParams) => {
1349
+ const code = typeof codeOrParams === "string" ? codeOrParams : codeOrParams.code;
1350
+ return this.regenerateRecoveryCodes(code);
1351
+ }
1352
+ };
1353
+ }
1354
+ /** MFA : démarre l'enrôlement TOTP et renvoie le `secret` et l'URL `otpauth_url` à afficher en QR code. */
1226
1355
  async mfaEnroll() {
1227
1356
  return this.request("POST", `${this.prefix}/mfa/setup`);
1228
1357
  }
1358
+ /** MFA : confirme l'enrôlement avec un premier code et renvoie les codes de récupération. */
1229
1359
  async mfaVerifySetup(code) {
1230
1360
  return this.request("POST", `${this.prefix}/mfa/setup/verify`, { code });
1231
1361
  }
1362
+ /**
1363
+ * MFA : termine une connexion qui a renvoyé un défi (`mfa_token` et code TOTP). Enregistre la session et émet `SIGNED_IN`.
1364
+ */
1232
1365
  async mfaVerify(req) {
1233
1366
  const res = await this.request("POST", `${this.prefix}/mfa/verify-login`, req);
1234
1367
  if (res.data) {
@@ -1237,6 +1370,7 @@ var AuthService = class extends core.BaseService {
1237
1370
  }
1238
1371
  return res;
1239
1372
  }
1373
+ /** MFA : désactive le MFA après confirmation par le mot de passe. */
1240
1374
  async mfaUnenroll(password) {
1241
1375
  return this.request("POST", `${this.prefix}/mfa/disable`, { password });
1242
1376
  }
@@ -1308,6 +1442,7 @@ var AuthService = class extends core.BaseService {
1308
1442
  return res;
1309
1443
  }
1310
1444
  // -- Email verification -----------------------------------------------------
1445
+ /** Renvoie l'e-mail de vérification, à `email` ou à l'adresse de l'utilisateur connecté. */
1311
1446
  async resendVerificationEmail(email) {
1312
1447
  const e = email ?? this.client.authStore.user?.email;
1313
1448
  if (!e) {
@@ -1319,24 +1454,34 @@ var AuthService = class extends core.BaseService {
1319
1454
  return this.request("POST", `${this.prefix}/resend-verification`, { email: e });
1320
1455
  }
1321
1456
  // -- Email OTP (Phase 2) --------------------------------------------------
1457
+ /**
1458
+ * Envoie un code à usage unique par e-mail ; à confirmer par `verifyOtp({ type: 'email' })`.
1459
+ */
1322
1460
  async signInWithEmailOtp(email) {
1323
1461
  return this.request("POST", `${this.prefix}/email-otp/send`, { email });
1324
1462
  }
1325
1463
  // -- SMS OTP (Phase 2) ----------------------------------------------------
1464
+ /** Envoie un code à usage unique par SMS ; à confirmer par `verifyOtp({ type: 'sms' })`. */
1326
1465
  async signInWithSmsOtp(phone) {
1327
1466
  return this.request("POST", `${this.prefix}/sms/send`, { phone });
1328
1467
  }
1329
1468
  // -- Identity linking (Phase 2) -------------------------------------------
1469
+ /** Liste les identités (fournisseurs) liées à l'utilisateur connecté. */
1330
1470
  async listIdentities() {
1331
1471
  return this.request("GET", `${this.prefix}/user/identities`);
1332
1472
  }
1473
+ /**
1474
+ * Démarre la liaison d'un fournisseur OAuth au compte connecté et renvoie l'URL d'autorisation à ouvrir.
1475
+ */
1333
1476
  async linkIdentity(provider) {
1334
1477
  return this.request("POST", `${this.prefix}/user/identities/link/${provider}`);
1335
1478
  }
1479
+ /** Détache une identité du compte connecté par son identifiant. */
1336
1480
  async unlinkIdentity(identityId) {
1337
1481
  return this.request("DELETE", `${this.prefix}/user/identities/${identityId}`);
1338
1482
  }
1339
1483
  // -- MFA recovery codes (Phase 2) -----------------------------------------
1484
+ /** MFA : régénère les codes de récupération après confirmation par un code TOTP. */
1340
1485
  async regenerateRecoveryCodes(code) {
1341
1486
  return this.request("POST", `${this.prefix}/mfa/recovery-codes/regenerate`, { code });
1342
1487
  }