@auth0/auth0-server-js 1.4.0 → 1.6.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.cjs CHANGED
@@ -26,13 +26,19 @@ __export(index_exports, {
26
26
  CookieTransactionStore: () => CookieTransactionStore,
27
27
  InvalidConfigurationError: () => InvalidConfigurationError,
28
28
  IssuerValidationError: () => IssuerValidationError,
29
+ MfaChallengeError: () => import_auth0_auth_js2.MfaChallengeError,
30
+ MfaEnrollmentError: () => import_auth0_auth_js2.MfaEnrollmentError,
31
+ MfaListAuthenticatorsError: () => import_auth0_auth_js2.MfaListAuthenticatorsError,
32
+ MfaVerifyError: () => import_auth0_auth_js2.MfaVerifyError,
29
33
  MissingRequiredArgumentError: () => MissingRequiredArgumentError,
30
34
  MissingSessionError: () => MissingSessionError,
31
35
  MissingTransactionError: () => MissingTransactionError,
32
36
  ServerClient: () => ServerClient,
37
+ ServerMfaClient: () => ServerMfaClient,
33
38
  StartLinkUserError: () => StartLinkUserError,
34
39
  StatefulStateStore: () => StatefulStateStore,
35
- StatelessStateStore: () => StatelessStateStore
40
+ StatelessStateStore: () => StatelessStateStore,
41
+ isMfaRequiredError: () => import_auth0_auth_js2.isMfaRequiredError
36
42
  });
37
43
  module.exports = __toCommonJS(index_exports);
38
44
 
@@ -95,6 +101,17 @@ var createUpdatedTokenSet = (audience, response) => ({
95
101
  expiresAt: response.expiresAt
96
102
  });
97
103
  function updateStateData(audience, stateData, tokenEndpointResponse, context) {
104
+ if (stateData && tokenEndpointResponse.claims) {
105
+ const newSub = tokenEndpointResponse.claims.sub;
106
+ const newIss = tokenEndpointResponse.claims.iss;
107
+ const existingSub = stateData.user?.sub;
108
+ const existingIss = stateData.user?.iss;
109
+ const subMismatch = newSub !== void 0 && existingSub !== void 0 && newSub !== existingSub;
110
+ const issMismatch = newIss !== void 0 && existingIss !== void 0 && newIss !== existingIss;
111
+ if (subMismatch || issMismatch) {
112
+ stateData = void 0;
113
+ }
114
+ }
98
115
  if (stateData) {
99
116
  const isNewTokenSet = !stateData.tokenSets.some(
100
117
  (tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope
@@ -172,10 +189,90 @@ function getTelemetryConfig(config) {
172
189
  return {
173
190
  enabled: true,
174
191
  name: config?.name ?? "@auth0/auth0-server-js",
175
- version: config?.version ?? "1.4.0"
192
+ version: config?.version ?? "1.6.0"
176
193
  };
177
194
  }
178
195
 
196
+ // src/mfa/server-mfa-client.ts
197
+ var ServerMfaClient = class {
198
+ #options;
199
+ /**
200
+ * @internal
201
+ */
202
+ constructor(options) {
203
+ this.#options = options;
204
+ }
205
+ /**
206
+ * Lists all MFA authenticators enrolled by the user.
207
+ *
208
+ * @param options - Options for listing authenticators
209
+ * @returns Promise resolving to an array of enrolled authenticators
210
+ * @throws {MfaListAuthenticatorsError} When the request fails
211
+ */
212
+ async listAuthenticators(options) {
213
+ return this.#options.authClient.mfa.listAuthenticators(options);
214
+ }
215
+ /**
216
+ * Enrolls a new MFA authenticator for the user.
217
+ *
218
+ * @param options - Enrollment options
219
+ * @returns Promise resolving to enrollment response with authenticator details
220
+ * @throws {MfaEnrollmentError} When enrollment fails
221
+ */
222
+ async enrollAuthenticator(options) {
223
+ return this.#options.authClient.mfa.enrollAuthenticator(options);
224
+ }
225
+ /**
226
+ * Initiates an MFA challenge for user verification.
227
+ *
228
+ * @param options - Challenge options
229
+ * @returns Promise resolving to challenge response with challenge details
230
+ * @throws {MfaChallengeError} When the challenge fails
231
+ */
232
+ async challengeAuthenticator(options) {
233
+ return this.#options.authClient.mfa.challengeAuthenticator(options);
234
+ }
235
+ /**
236
+ * Verifies an MFA challenge and completes the authentication flow.
237
+ *
238
+ * Exchanges the MFA token and verification code for access, ID, and refresh tokens,
239
+ * then saves them into the user's session automatically.
240
+ *
241
+ * @param options - The MFA token, factor type (otp / oob / recovery-code), and the code to verify
242
+ * @param storeOptions - Optional options forwarded to the session store. Can be omitted when
243
+ * using the built-in stores; required if your custom store needs extra context (e.g. a request object).
244
+ * @returns The tokens returned by Auth0 after successful verification
245
+ * @throws {MfaVerifyError} When verification fails (e.g. invalid token, wrong code)
246
+ */
247
+ async verify(options, storeOptions) {
248
+ const tokenResponse = await this.#options.authClient.mfa.verify(options);
249
+ const audience = options.audience ?? this.#options.defaultAudience;
250
+ const existingStateData = await this.#options.stateStore.get(
251
+ this.#options.stateStoreIdentifier,
252
+ storeOptions
253
+ );
254
+ const updatedStateData = updateStateData(audience, existingStateData, tokenResponse, {
255
+ domain: this.#options.domain
256
+ });
257
+ await this.#options.stateStore.set(
258
+ this.#options.stateStoreIdentifier,
259
+ updatedStateData,
260
+ true,
261
+ storeOptions
262
+ );
263
+ const result = {
264
+ accessToken: tokenResponse.accessToken,
265
+ tokenType: tokenResponse.tokenType ?? "bearer",
266
+ expiresAt: tokenResponse.expiresAt,
267
+ scope: tokenResponse.scope
268
+ };
269
+ if (tokenResponse.idToken) result.idToken = tokenResponse.idToken;
270
+ if (tokenResponse.refreshToken) result.refreshToken = tokenResponse.refreshToken;
271
+ if (tokenResponse.recoveryCode) result.recoveryCode = tokenResponse.recoveryCode;
272
+ return result;
273
+ }
274
+ };
275
+
179
276
  // src/server-client.ts
180
277
  var DEFAULT_SCOPES = "openid profile email offline_access";
181
278
  var normalizeDomain = (value) => {
@@ -210,6 +307,7 @@ var ServerClient = class {
210
307
  #authClientOptions;
211
308
  #staticDomain;
212
309
  #authClient;
310
+ #mfaClient;
213
311
  /**
214
312
  * The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
215
313
  * Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
@@ -226,6 +324,24 @@ var ServerClient = class {
226
324
  }
227
325
  return this.#authClient;
228
326
  }
327
+ /**
328
+ * The MFA client for managing multi-factor authentication operations.
329
+ *
330
+ * Provides methods to list, enroll, and challenge MFA authenticators,
331
+ * as well as verify MFA challenges to complete authentication.
332
+ *
333
+ * The `verify` method integrates with the session state store, persisting tokens
334
+ * and user data after successful MFA verification.
335
+ *
336
+ * This property can only be used when `domain` is configured as a static string.
337
+ * In resolver mode (`domain` as a function), MFA is not supported.
338
+ */
339
+ get mfa() {
340
+ if (!this.#mfaClient) {
341
+ throw new InvalidConfigurationError("mfa is only available when using a static domain configuration.");
342
+ }
343
+ return this.#mfaClient;
344
+ }
229
345
  constructor(options) {
230
346
  this.#options = options;
231
347
  this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
@@ -259,6 +375,13 @@ var ServerClient = class {
259
375
  ...this.#authClientOptions,
260
376
  telemetry: getTelemetryConfig(this.#options.telemetry)
261
377
  });
378
+ this.#mfaClient = new ServerMfaClient({
379
+ authClient: this.#authClient,
380
+ domain,
381
+ stateStore: this.#stateStore,
382
+ stateStoreIdentifier: this.#stateStoreIdentifier,
383
+ defaultAudience: this.#options.authorizationParams?.audience ?? "default"
384
+ });
262
385
  }
263
386
  }
264
387
  async #resolveDomain(storeOptions) {
@@ -568,7 +691,7 @@ var ServerClient = class {
568
691
  * Also updates the store when a new token was retrieved from Auth0.
569
692
  * @param storeOptions Optional options used to pass to the Transaction and State Store.
570
693
  *
571
- * @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token.
694
+ * @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token. When the cause is `mfa_required`, use `isMfaRequiredError(error)` to narrow the error and read `cause.mfa_token`.
572
695
  *
573
696
  * @returns The Token Set, containing the access token, as well as additional information.
574
697
  */
@@ -703,6 +826,65 @@ var ServerClient = class {
703
826
  }
704
827
  return authClient.buildLogoutUrl(options);
705
828
  }
829
+ /**
830
+ * Exchanges a custom token for Auth0 tokens and persists the resulting session (RFC 8693).
831
+ *
832
+ * Calls the token endpoint using the RFC 8693 Token Exchange grant, then stores the
833
+ * resulting tokens in the StateStore — effectively logging the user in without an
834
+ * interactive browser flow. Use this when the caller already holds a trusted external
835
+ * token (e.g. a Google ID token, a legacy system token) and wants to establish an
836
+ * Auth0 session from it.
837
+ *
838
+ * Requires a Token Exchange Profile configured in your Auth0 tenant.
839
+ *
840
+ * @param options Options for the custom token exchange, including the subject token and its type.
841
+ * @param storeOptions Optional options passed to the StateStore.
842
+ *
843
+ * @throws {TokenExchangeError} If the exchange fails or the subject token is invalid.
844
+ * @throws {MissingClientAuthError} If client credentials are not configured.
845
+ *
846
+ * @returns A promise resolving to an object containing `authorizationDetails` when RAR was used.
847
+ */
848
+ async loginWithCustomTokenExchange(options, storeOptions) {
849
+ const domain = await this.#resolveDomain(storeOptions);
850
+ const authClient = this.#getAuthClient(domain);
851
+ const tokenEndpointResponse = await authClient.exchangeToken({
852
+ ...options,
853
+ scope: ensureOpenIdScope(options.scope)
854
+ });
855
+ const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
856
+ const stateData = updateStateData(
857
+ this.#options.authorizationParams?.audience ?? "default",
858
+ existingStateData,
859
+ tokenEndpointResponse,
860
+ { domain }
861
+ );
862
+ await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
863
+ return { authorizationDetails: tokenEndpointResponse.authorizationDetails };
864
+ }
865
+ /**
866
+ * Exchanges a custom token for Auth0 tokens without establishing a session (RFC 8693).
867
+ *
868
+ * Performs the same RFC 8693 Token Exchange as `loginWithCustomTokenExchange` but
869
+ * returns the raw token response without writing anything to the StateStore. Use this
870
+ * for delegation or impersonation flows where you need downstream tokens but do not
871
+ * want to create or modify the current user session.
872
+ *
873
+ * Requires a Token Exchange Profile configured in your Auth0 tenant.
874
+ *
875
+ * @param options Options for the custom token exchange, including the subject token and its type.
876
+ * @param storeOptions Optional options passed to the StateStore (used only for domain resolution in resolver mode).
877
+ *
878
+ * @throws {TokenExchangeError} If the exchange fails or the subject token is invalid.
879
+ * @throws {MissingClientAuthError} If client credentials are not configured.
880
+ *
881
+ * @returns A promise resolving to the token response from Auth0.
882
+ */
883
+ async customTokenExchange(options, storeOptions) {
884
+ const domain = await this.#resolveDomain(storeOptions);
885
+ const authClient = this.#getAuthClient(domain);
886
+ return authClient.exchangeToken(options);
887
+ }
706
888
  /**
707
889
  * Handles the backchannel logout process by verifying the logout token and deleting the session from the store if the logout token was considered valid.
708
890
  * @param logoutToken The logout token to verify and use to delete the session from the store.
@@ -1049,6 +1231,9 @@ var StatelessStateStore = class extends AbstractSessionStore {
1049
1231
  };
1050
1232
  }
1051
1233
  };
1234
+
1235
+ // src/mfa/index.ts
1236
+ var import_auth0_auth_js2 = require("@auth0/auth0-auth-js");
1052
1237
  // Annotate the CommonJS export names for ESM import in node:
1053
1238
  0 && (module.exports = {
1054
1239
  AbstractStateStore,
@@ -1057,12 +1242,18 @@ var StatelessStateStore = class extends AbstractSessionStore {
1057
1242
  CookieTransactionStore,
1058
1243
  InvalidConfigurationError,
1059
1244
  IssuerValidationError,
1245
+ MfaChallengeError,
1246
+ MfaEnrollmentError,
1247
+ MfaListAuthenticatorsError,
1248
+ MfaVerifyError,
1060
1249
  MissingRequiredArgumentError,
1061
1250
  MissingSessionError,
1062
1251
  MissingTransactionError,
1063
1252
  ServerClient,
1253
+ ServerMfaClient,
1064
1254
  StartLinkUserError,
1065
1255
  StatefulStateStore,
1066
- StatelessStateStore
1256
+ StatelessStateStore,
1257
+ isMfaRequiredError
1067
1258
  });
1068
1259
  //# sourceMappingURL=index.cjs.map