@nauth-toolkit/client 0.1.108 → 0.1.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -164,8 +164,7 @@ var defaultEndpoints = {
164
164
  mfaDevices: "/mfa/devices",
165
165
  mfaSetupData: "/mfa/setup-data",
166
166
  mfaVerifySetup: "/mfa/verify-setup",
167
- mfaRemove: "/mfa/method",
168
- mfaPreferred: "/mfa/preferred-method",
167
+ mfaPreferred: "/mfa/devices/:deviceId/preferred",
169
168
  mfaBackupCodes: "/mfa/backup-codes/generate",
170
169
  socialLinked: "/social/linked",
171
170
  socialLink: "/social/link",
@@ -192,8 +191,8 @@ var defaultAdminEndpoints = {
192
191
  getUserSessions: "/users/:sub/sessions",
193
192
  logoutAll: "/users/:sub/logout-all",
194
193
  getMfaStatus: "/users/:sub/mfa/status",
195
- setPreferredMfaMethod: "/mfa/preferred-method",
196
- removeMfaDevices: "/mfa/remove-devices",
194
+ removeMfaDeviceById: "/mfa/devices/:deviceId",
195
+ setPreferredMfaDevice: "/users/:sub/mfa/devices/:deviceId/preferred",
197
196
  setMfaExemption: "/mfa/exemption",
198
197
  getAuditHistory: "/audit/history"
199
198
  };
@@ -1155,35 +1154,51 @@ var AdminOperations = class {
1155
1154
  * Set preferred MFA method for a user
1156
1155
  *
1157
1156
  * @param sub - User UUID
1158
- * @param method - MFA method to set as preferred
1157
+ * Remove MFA devices for a user
1158
+ *
1159
+ * @param sub - User UUID
1160
+ * @param method - MFA method to remove
1159
1161
  * @returns Success message
1160
1162
  * @throws {NAuthClientError} If operation fails
1161
1163
  *
1162
1164
  * @example
1163
1165
  * ```typescript
1164
- * await client.admin.setPreferredMfaMethod('user-uuid', 'totp');
1166
+ * await client.admin.removeMfaDevices('user-uuid', 'sms');
1167
+ * ```
1168
+ */
1169
+ /**
1170
+ * Remove a single MFA device by device ID (admin).
1171
+ *
1172
+ * @param deviceId - MFA device ID
1173
+ * @returns Removal result
1174
+ * @throws {NAuthClientError} If operation fails
1175
+ *
1176
+ * @example
1177
+ * ```typescript
1178
+ * const result = await client.admin.removeMfaDeviceById(123);
1179
+ * console.log(result.removedDeviceId);
1165
1180
  * ```
1166
1181
  */
1167
- async setPreferredMfaMethod(sub, method) {
1168
- const path = this.buildAdminUrl(this.adminEndpoints.setPreferredMfaMethod);
1169
- return this.post(path, { sub, method });
1182
+ async removeMfaDeviceById(deviceId) {
1183
+ const path = this.buildAdminUrl(this.adminEndpoints.removeMfaDeviceById, { deviceId: String(deviceId) });
1184
+ return this.delete(path);
1170
1185
  }
1171
1186
  /**
1172
- * Remove MFA devices for a user
1187
+ * Set preferred MFA device for a user (admin operation).
1173
1188
  *
1174
- * @param sub - User UUID
1175
- * @param method - MFA method to remove
1189
+ * @param sub - User identifier
1190
+ * @param deviceId - Device ID to set as preferred
1176
1191
  * @returns Success message
1177
1192
  * @throws {NAuthClientError} If operation fails
1178
1193
  *
1179
1194
  * @example
1180
1195
  * ```typescript
1181
- * await client.admin.removeMfaDevices('user-uuid', 'sms');
1196
+ * await client.admin.setPreferredMfaDevice('user-uuid', 123);
1182
1197
  * ```
1183
1198
  */
1184
- async removeMfaDevices(sub, method) {
1185
- const path = this.buildAdminUrl(this.adminEndpoints.removeMfaDevices);
1186
- return this.post(path, { sub, method });
1199
+ async setPreferredMfaDevice(sub, deviceId) {
1200
+ const path = this.buildAdminUrl(this.adminEndpoints.setPreferredMfaDevice, { sub, deviceId: String(deviceId) });
1201
+ return this.post(path, {});
1187
1202
  }
1188
1203
  /**
1189
1204
  * Grant or revoke MFA exemption for a user
@@ -1341,12 +1356,7 @@ var AdminOperations = class {
1341
1356
  } catch {
1342
1357
  }
1343
1358
  }
1344
- const mutatingMethods = [
1345
- "POST",
1346
- "PUT",
1347
- "PATCH",
1348
- "DELETE"
1349
- ];
1359
+ const mutatingMethods = ["POST", "PUT", "PATCH", "DELETE"];
1350
1360
  if (this.config.tokenDelivery === "cookies" && hasWindow() && mutatingMethods.includes(method)) {
1351
1361
  const csrfToken = this.getCsrfToken();
1352
1362
  if (csrfToken) {
@@ -1363,9 +1373,7 @@ var AdminOperations = class {
1363
1373
  */
1364
1374
  getCsrfToken() {
1365
1375
  if (!hasWindow() || typeof document === "undefined") return null;
1366
- const match = document.cookie.match(
1367
- new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`)
1368
- );
1376
+ const match = document.cookie.match(new RegExp(`(^| )${this.config.csrf.cookieName}=([^;]+)`));
1369
1377
  return match ? decodeURIComponent(match[2]) : null;
1370
1378
  }
1371
1379
  /**
@@ -1495,6 +1503,12 @@ var NAuthClient = class {
1495
1503
  __publicField(this, "challengeRouter");
1496
1504
  __publicField(this, "oauthStorage");
1497
1505
  __publicField(this, "currentUser", null);
1506
+ /**
1507
+ * Internally tracked MFA device ID
1508
+ * When user selects a specific device (e.g., "Microsoft Authenticator" vs "Google Authenticator"),
1509
+ * SDK stores it here and auto-injects into respondToChallenge()
1510
+ */
1511
+ __publicField(this, "selectedDeviceId");
1498
1512
  /**
1499
1513
  * Admin operations (available if admin config provided).
1500
1514
  *
@@ -1755,6 +1769,9 @@ var NAuthClient = class {
1755
1769
  * @throws {NAuthClientError} If validation fails
1756
1770
  */
1757
1771
  async respondToChallenge(response) {
1772
+ if (this.selectedDeviceId !== void 0 && response.type === "MFA_REQUIRED" /* MFA_REQUIRED */ && (response.method === "totp" || response.method === "passkey")) {
1773
+ response.deviceId = this.selectedDeviceId;
1774
+ }
1758
1775
  if (response.type === "MFA_SETUP_REQUIRED" /* MFA_SETUP_REQUIRED */ && response.method === "totp") {
1759
1776
  const setupData = response.setupData;
1760
1777
  if (!setupData || typeof setupData !== "object") {
@@ -1784,6 +1801,9 @@ var NAuthClient = class {
1784
1801
  try {
1785
1802
  const result = await this.post(this.config.endpoints.respondChallenge, response);
1786
1803
  await this.handleAuthResponse(result);
1804
+ if (result.user && !result.challengeName) {
1805
+ this.selectedDeviceId = void 0;
1806
+ }
1787
1807
  if (result.challengeName) {
1788
1808
  const challengeEvent = { type: "auth:challenge", data: result, timestamp: Date.now() };
1789
1809
  this.eventEmitter.emit(challengeEvent);
@@ -1803,6 +1823,65 @@ var NAuthClient = class {
1803
1823
  throw authError;
1804
1824
  }
1805
1825
  }
1826
+ /**
1827
+ * Select an MFA device for verification
1828
+ *
1829
+ * Call this when user selects a specific device from the MFA selector UI.
1830
+ * SDK stores the deviceId internally and auto-injects it when respondToChallenge() is called.
1831
+ *
1832
+ * @param deviceId - ID of the device user selected
1833
+ *
1834
+ * @example
1835
+ * ```typescript
1836
+ * // User clicks "Microsoft Authenticator" button
1837
+ * client.selectMFADevice(48);
1838
+ *
1839
+ * // Later, when submitting OTP code:
1840
+ * await client.respondToChallenge({
1841
+ * type: 'MFA_REQUIRED',
1842
+ * session: 'abc123',
1843
+ * method: 'totp',
1844
+ * code: '123456',
1845
+ * // SDK auto-injects deviceId=48 here!
1846
+ * });
1847
+ * ```
1848
+ */
1849
+ selectMFADevice(deviceId) {
1850
+ this.selectedDeviceId = deviceId;
1851
+ }
1852
+ /**
1853
+ * Get available MFA devices from challenge response
1854
+ *
1855
+ * Returns array of devices for methods that support multiple devices (TOTP, Passkey).
1856
+ * Use this to render device selection UI only.
1857
+ *
1858
+ * @param challenge - Challenge response from login/signup
1859
+ * @returns Array of MFA devices with id, name, and type
1860
+ *
1861
+ * @example
1862
+ * ```typescript
1863
+ * const devices = client.getMFADevices(challengeResponse);
1864
+ * // Returns: [
1865
+ * // { id: 48, name: "Microsoft Authenticator", type: "totp" },
1866
+ * // { id: 3, name: "Google Authenticator", type: "totp" }
1867
+ * // ]
1868
+ * ```
1869
+ */
1870
+ getMFADevices(challenge) {
1871
+ const devices = challenge.challengeParameters?.["devices"];
1872
+ if (Array.isArray(devices)) {
1873
+ return devices;
1874
+ }
1875
+ return [];
1876
+ }
1877
+ /**
1878
+ * Clear any selected MFA device
1879
+ *
1880
+ * Useful if user navigates back to device selector or cancels MFA flow.
1881
+ */
1882
+ clearSelectedDevice() {
1883
+ this.selectedDeviceId = void 0;
1884
+ }
1806
1885
  /**
1807
1886
  * Resend a challenge code.
1808
1887
  */
@@ -1952,20 +2031,43 @@ var NAuthClient = class {
1952
2031
  );
1953
2032
  }
1954
2033
  /**
1955
- * Remove MFA method.
2034
+ * Remove ALL MFA devices for a specific method type.
2035
+ *
2036
+ * ⚠️ **Warning**: This removes ALL devices of the specified method.
2037
+ * For example, if you have 3 TOTP devices, this will remove all 3.
2038
+ *
2039
+ * **Prefer `removeMfaDeviceById()`** to remove individual devices.
2040
+ *
2041
+ * @param method - MFA method type ('totp', 'sms', 'email', 'passkey')
2042
+ * @returns Success message
2043
+ *
2044
+ * @example
2045
+ * ```typescript
2046
+ * // Removes ALL TOTP devices (all authenticator apps)
2047
+ * await client.removeMfaDevice('totp');
2048
+ * ```
1956
2049
  */
1957
- async removeMfaDevice(method) {
1958
- const path = `${this.config.endpoints.mfaRemove}/${method}`;
2050
+ /**
2051
+ * Remove a single MFA device by device ID.
2052
+ *
2053
+ * @param deviceId - MFA device ID
2054
+ * @returns Removal response
2055
+ */
2056
+ async removeMfaDeviceById(deviceId) {
2057
+ const path = `${this.config.endpoints.mfaDevices}/${deviceId}`;
1959
2058
  return this.delete(path, true);
1960
2059
  }
1961
2060
  /**
1962
- * Set preferred MFA method.
2061
+ * Set a specific MFA device as preferred.
2062
+ *
2063
+ * This marks the device as preferred and updates the user's preferred MFA method.
1963
2064
  *
1964
- * @param method - Device method to set as preferred ('totp', 'sms', 'email', or 'passkey'). Cannot be 'backup'.
2065
+ * @param deviceId - MFA device ID
1965
2066
  * @returns Success message
1966
2067
  */
1967
- async setPreferredMfaMethod(method) {
1968
- return this.post(this.config.endpoints.mfaPreferred, { methodType: method }, true);
2068
+ async setPreferredMfaDevice(deviceId) {
2069
+ const path = `${this.config.endpoints.mfaDevices}/${deviceId}/preferred`;
2070
+ return this.post(path, {}, true);
1969
2071
  }
1970
2072
  /**
1971
2073
  * Generate backup codes.