@auth0/nextjs-auth0
Version:
Auth0 Next.js SDK
294 lines (293 loc) • 9.09 kB
JavaScript
import { SdkError } from "./sdk-error.js";
/**
* Base class for all MFA-related errors.
* Provides standardized JSON serialization matching Auth0 API format.
*
* Supports two consumption paths with identical shape:
* 1. Direct SDK call: properties accessed on error instance
* 2. HTTP API route: Response.json(error) uses toJSON() automatically
*
* @example
* ```typescript
* try {
* await mfa.getAuthenticators({ mfaToken });
* } catch (error) {
* if (error instanceof MfaError) {
* return Response.json(error, { status: 400 });
* }
* }
* ```
*/
class MfaError extends SdkError {
/**
* Serialize error for HTTP responses.
* Called automatically by Response.json() and JSON.stringify().
* Ensures both SDK and HTTP API consumers get identical shape.
*/
toJSON() {
return {
error: this.error,
error_description: this.error_description
};
}
get code() {
return this.error;
}
}
/**
* Thrown when request validation fails (missing/invalid params).
* Mapped to 400 Bad Request.
*/
export class InvalidRequestError extends SdkError {
constructor(message) {
super(message);
this.code = "invalid_request";
Object.setPrototypeOf(this, InvalidRequestError.prototype);
this.name = "InvalidRequestError";
}
toJSON() {
return {
error: this.code,
error_description: this.message
};
}
}
/**
* Error thrown when listing MFA authenticators fails.
*
* @example
* ```typescript
* try {
* const authenticators = await mfa.getAuthenticators({ mfaToken });
* } catch (error) {
* if (error instanceof MfaGetAuthenticatorsError) {
* console.error(error.code); // 'invalid_token', 'expired_token', etc.
* console.error(error.cause?.error_description);
* }
* }
* ```
*/
export class MfaGetAuthenticatorsError extends MfaError {
constructor(error, error_description, cause) {
super(error_description);
this.name = "MfaGetAuthenticatorsError";
this.error = error;
this.error_description = error_description;
this.cause = cause;
Object.setPrototypeOf(this, MfaGetAuthenticatorsError.prototype);
}
}
/**
* Error thrown when initiating an MFA challenge fails.
*
* @example
* ```typescript
* try {
* await mfa.challenge({
* mfaToken,
* challengeType: 'oob',
* authenticatorId: 'sms|dev_abc123'
* });
* } catch (error) {
* if (error instanceof MfaChallengeError) {
* if (error.cause?.error === 'invalid_authenticator_id') {
* console.error('Authenticator not found or not active');
* }
* }
* }
* ```
*/
export class MfaChallengeError extends MfaError {
constructor(error, error_description, cause) {
super(error_description);
this.name = "MfaChallengeError";
this.error = error;
this.error_description = error_description;
this.cause = cause;
Object.setPrototypeOf(this, MfaChallengeError.prototype);
}
}
/**
* Error thrown when MFA verification fails.
*
* @example
* ```typescript
* try {
* await mfa.verify({
* mfaToken,
* otp: '123456'
* });
* } catch (error) {
* if (error instanceof MfaVerifyError) {
* if (error.cause?.error === 'invalid_grant') {
* console.error('Invalid or expired verification code');
* }
* }
* }
* ```
*/
export class MfaVerifyError extends MfaError {
constructor(error, error_description, cause) {
super(error_description);
this.name = "MfaVerifyError";
this.error = error;
this.error_description = error_description;
this.cause = cause;
Object.setPrototypeOf(this, MfaVerifyError.prototype);
}
}
/**
* Error thrown when no MFA factors are available for challenge.
* SDK-generated error (no Auth0 API equivalent).
*/
export class MfaNoAvailableFactorsError extends SdkError {
constructor(error_description) {
super(error_description);
this.code = "mfa_no_available_factors";
this.error = "mfa_no_available_factors";
this.name = "MfaNoAvailableFactorsError";
this.error_description = error_description;
Object.setPrototypeOf(this, MfaNoAvailableFactorsError.prototype);
}
}
/**
* Error thrown when MFA enrollment fails.
*
* @example
* ```typescript
* try {
* await mfa.enroll({
* mfaToken,
* authenticatorTypes: ['otp']
* });
* } catch (error) {
* if (error instanceof MfaEnrollmentError) {
* if (error.cause?.error === 'unsupported_challenge_type') {
* console.error('Tenant does not support OTP enrollment');
* }
* }
* }
* ```
*/
export class MfaEnrollmentError extends MfaError {
constructor(error, error_description, cause) {
super(error_description);
this.name = "MfaEnrollmentError";
this.error = error;
this.error_description = error_description;
this.cause = cause;
Object.setPrototypeOf(this, MfaEnrollmentError.prototype);
}
}
/**
* Thrown when {@link getAccessToken} requires MFA step-up authentication.
*
* This error is thrown during token refresh when Auth0 returns `mfa_required`.
* The {@link mfa_token} property contains an encrypted token that can be used
* with Auth0's MFA API to complete the authentication challenge.
*
* @remarks
* The `mfa_token` is encrypted using the SDK's cookie secret for security.
* The raw token from Auth0 is never exposed to application code.
*
* Supports two consumption paths with identical shape:
* 1. Direct SDK call: properties accessed on error instance
* 2. HTTP API route: Response.json(error) uses toJSON() automatically
*
* @example Handling MFA required in a route handler
* ```typescript
* import { getAccessToken, MfaRequiredError } from "@auth0/nextjs-auth0/server";
*
* try {
* const { token } = await getAccessToken({ audience: "https://api.example.com" });
* } catch (error) {
* if (error instanceof MfaRequiredError) {
* // Redirect to MFA challenge page
* redirect(`/mfa?token=${error.mfa_token}`);
* }
* throw error;
* }
* ```
*
* @see {@link https://auth0.com/docs/api/authentication#multi-factor-authentication Auth0 MFA API}
*/
export class MfaRequiredError extends SdkError {
/**
* @param error_description - Error description from Auth0
* @param mfaToken - Encrypted MFA token (constructor param uses camelCase)
* @param mfaRequirements - MFA requirements from Auth0 (constructor param uses camelCase)
* @param cause - Underlying error
*
* @remarks
* Constructor parameters use camelCase (mfaToken, mfaRequirements) for consistency
* with SDK conventions, but they are assigned to snake_case properties (mfa_token,
* mfa_requirements) to match Auth0 API response format.
*/
constructor(error_description, mfaToken, mfaRequirements, cause) {
super(error_description);
this.code = "mfa_required";
/** Original Auth0 error code */
this.error = "mfa_required";
this.name = "MfaRequiredError";
this.error_description = error_description;
this.mfa_token = mfaToken;
this.mfa_requirements = mfaRequirements;
this.cause = cause;
}
/**
* Serialize error for HTTP responses.
* Called automatically by Response.json() and JSON.stringify().
* Ensures both SDK and HTTP API consumers get identical shape.
*/
toJSON() {
return {
error: this.error,
error_description: this.error_description,
mfa_token: this.mfa_token,
...(this.mfa_requirements && { mfa_requirements: this.mfa_requirements })
};
}
}
/**
* Thrown when MFA API methods are called but no context exists in session
* for the provided encrypted mfa_token.
*
* This typically occurs when:
* - The session expired between catching MfaRequiredError and calling MFA methods
* - The mfa_token was modified or is from a different session
* - The MFA context was cleaned up due to TTL expiration
*
* @example
* ```typescript
* try {
* await auth0.completeMfaChallenge(mfaToken, code);
* } catch (error) {
* if (error instanceof MfaTokenNotFoundError) {
* // Restart MFA flow - context was lost
* redirect("/auth/login?prompt=mfa");
* }
* }
* ```
*/
export class MfaTokenExpiredError extends SdkError {
constructor() {
super("MFA token has expired. Please restart the MFA flow.");
this.code = "mfa_token_expired";
this.name = "MfaTokenExpiredError";
}
}
/**
* Thrown when the encrypted mfa_token is invalid.
*
* This occurs when:
* - The token was tampered with
* - The token is malformed (not valid JWE)
* - The token was encrypted with a different secret
*/
export class MfaTokenInvalidError extends SdkError {
constructor() {
super("MFA token is invalid.");
this.code = "mfa_token_invalid";
this.name = "MfaTokenInvalidError";
}
}