UNPKG

@auth0/nextjs-auth0

Version:
537 lines (536 loc) 16.6 kB
/** * Shared MFA test scenarios for flow tests. * Reduces duplication between client and server test suites. */ // ============================================================================ // getAuthenticators Scenarios // ============================================================================ export const getAuthenticatorsScenarios = [ { name: "list all authenticators (3 types)", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [{ type: "otp" }, { type: "oob" }, { type: "recovery-code" }] } }, mswResponse: { status: 200, body: [ { id: "auth_123", authenticator_type: "otp", type: "otp", active: true, name: "Google Authenticator" }, { id: "auth_456", authenticator_type: "oob", type: "oob", active: true, oob_channel: "sms", phone_number: "+1***5678" }, { id: "auth_789", authenticator_type: "recovery-code", type: "recovery-code", active: true } ] }, expected: (result) => { if (result.length !== 3) throw new Error("Expected 3 authenticators"); if (result[0].type !== "otp") throw new Error("Expected otp first"); if (result[1].type !== "oob") throw new Error("Expected oob second"); if (result[2].type !== "recovery-code") throw new Error("Expected recovery-code third"); } }, { name: "filter by single challenge type (otp)", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 200, body: [ { id: "auth_123", authenticator_type: "otp", type: "otp", active: true } ] }, expected: (result) => { if (result.length !== 1) throw new Error("Expected 1 authenticator"); if (result[0].type !== "otp") throw new Error("Expected otp only"); } }, { name: "empty authenticators array", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 200, body: [] }, expected: [] }, { name: "empty challenge types (no filtering - fallback)", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [] } }, mswResponse: { status: 200, body: [ { id: "auth_123", authenticator_type: "otp", type: "otp", active: true } ] }, expected: (result) => { if (result.length !== 1) throw new Error("Expected 1 authenticator (fallback behavior)"); } }, { name: "missing challenge field (no filtering)", input: { mfaToken: "encrypted-token", mfaRequirements: {} }, mswResponse: { status: 200, body: [ { id: "auth_123", authenticator_type: "otp", type: "otp", active: true } ] }, expected: (result) => { if (result.length !== 1) throw new Error("Expected 1 authenticator"); } }, { name: "Auth0 400 error", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 400, body: { error: "invalid_grant", error_description: "Invalid MFA token" } }, expectError: (error) => { if (error.code !== "invalid_grant") throw new Error("Expected invalid_grant"); } }, { name: "Auth0 500 error", input: { mfaToken: "encrypted-token", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 500, body: { error: "server_error", error_description: "Failed to list authenticators" } }, expectError: (error) => { if (!error.message.includes("authenticators")) throw new Error("Expected error about authenticators"); } } ]; // ============================================================================ // challenge Scenarios // ============================================================================ export const challengeScenarios = [ { name: "OTP challenge", input: { mfaToken: "encrypted-token", challengeType: "otp", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 200, body: { challenge_type: "otp", challengeType: "otp" } }, expected: (result) => { if (result.challengeType !== "otp") throw new Error("Expected challengeType=otp"); } }, { name: "OOB challenge", input: { mfaToken: "encrypted-token", challengeType: "oob", mfaRequirements: { challenge: [{ type: "oob" }] } }, mswResponse: { status: 200, body: { challenge_type: "oob", challengeType: "oob", oob_code: "abc123", oobCode: "abc123", binding_method: "prompt", bindingMethod: "prompt" } }, expected: (result) => { if (result.challengeType !== "oob") throw new Error("Expected challengeType=oob"); if (result.oobCode !== "abc123") throw new Error("Expected oobCode=abc123"); if (result.bindingMethod !== "prompt") throw new Error("Expected bindingMethod=prompt"); } }, { name: "with authenticatorId", input: { mfaToken: "encrypted-token", challengeType: "otp", authenticatorId: "auth_123", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 200, body: { challenge_type: "otp", challengeType: "otp" } }, expected: (result) => { if (result.challengeType !== "otp") throw new Error("Expected otp"); } }, { name: "empty challenge types", input: { mfaToken: "encrypted-token", challengeType: "otp", mfaRequirements: { challenge: [] } }, mswResponse: { status: 400, body: { error: "mfa_no_available_factors", error_description: "No challenge types available for MFA" } }, expectError: (error) => { if (!error.message.includes("available")) throw new Error("Expected 'available' in error"); } }, { name: "Auth0 400 error", input: { mfaToken: "encrypted-token", challengeType: "invalid", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 400, body: { error: "invalid_request", error_description: "Invalid challenge type" } }, expectError: (error) => { if (error.code !== "invalid_request") throw new Error("Expected invalid_request"); } } ]; // ============================================================================ // verify Scenarios // ============================================================================ export const verifyScenarios = [ { name: "OTP verification success", input: { mfaToken: "encrypted-token", otp: "123456", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 200, body: { access_token: "new-access-token", refresh_token: "new-refresh-token", token_type: "Bearer", expires_in: 3600 } }, expected: (result) => { if (result.access_token !== "new-access-token") throw new Error("Expected access_token"); if (result.token_type !== "Bearer") throw new Error("Expected token_type=Bearer"); } }, { name: "OOB verification success", input: { mfaToken: "encrypted-token", oobCode: "abc123", bindingCode: "456", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 200, body: { access_token: "new-access-token", token_type: "Bearer", expires_in: 3600 } }, expected: (result) => { if (result.access_token !== "new-access-token") throw new Error("Expected access_token"); } }, { name: "Recovery code verification with new code", input: { mfaToken: "encrypted-token", recoveryCode: "abcd-efgh", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 200, body: { access_token: "new-access-token", token_type: "Bearer", expires_in: 3600, recovery_code: "new-recovery-code" } }, expected: (result) => { if (result.access_token !== "new-access-token") throw new Error("Expected access_token"); if (result.recovery_code !== "new-recovery-code") throw new Error("Expected recovery_code"); } }, { name: "Recovery code absent (tenant config)", input: { mfaToken: "encrypted-token", otp: "123456", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 200, body: { access_token: "new-access-token", token_type: "Bearer", expires_in: 3600 } }, expected: (result) => { if (result.recovery_code !== undefined) throw new Error("Expected no recovery_code"); } }, { name: "Wrong OTP → chained MFA", input: { mfaToken: "encrypted-token", otp: "000000", audience: "https://api.example.com", scope: "openid profile", mfaRequirements: { challenge: [{ type: "otp" }] } }, mswResponse: { status: 400, body: { error: "mfa_required", error_description: "Invalid OTP, retry required", mfa_token: "new-raw-mfa-token", mfa_requirements: { challenge: [{ type: "otp" }] } } }, expectError: (error) => { if (error.code !== "mfa_required") throw new Error("Expected mfa_required"); if (!error.mfa_token) throw new Error("Expected encrypted mfaToken"); if (!error.mfa_requirements) throw new Error("Expected mfaRequirements"); } }, { name: "Wrong binding code", input: { mfaToken: "encrypted-token", oobCode: "abc123", bindingCode: "999", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 400, body: { error: "invalid_grant", error_description: "Invalid binding code" } }, expectError: (error) => { if (error.code !== "invalid_grant") throw new Error("Expected invalid_grant"); } }, { name: "Rate limit (429)", input: { mfaToken: "encrypted-token", otp: "123456", audience: "https://api.example.com", scope: "openid profile" }, mswResponse: { status: 429, body: { error: "too_many_attempts", error_description: "Too many attempts" } }, expectError: (error) => { if (error.code !== "too_many_attempts") throw new Error("Expected too_many_attempts"); } } ]; // ============================================================================ // enroll Scenarios // ============================================================================ export const enrollScenarios = [ { name: "OTP enrollment", input: { mfaToken: "encrypted-token", authenticatorTypes: ["otp"] }, mswResponse: { status: 200, body: { authenticator_type: "otp", barcode_uri: "otpauth://totp/...", secret: "base32secret" } }, expected: (result) => { if (result.authenticatorType !== "otp") throw new Error("Expected authenticatorType=otp"); if (!result.barcodeUri) throw new Error("Expected barcodeUri"); if (!result.secret) throw new Error("Expected secret"); } }, { name: "OOB SMS enrollment", input: { mfaToken: "encrypted-token", authenticatorTypes: ["oob"], oobChannels: ["sms"], phoneNumber: "+15551234567" }, mswResponse: { status: 200, body: { authenticator_type: "oob", oob_channel: "sms", oob_code: "abc123" } }, expected: (result) => { if (result.authenticatorType !== "oob") throw new Error("Expected authenticatorType=oob"); if (result.oobChannel !== "sms") throw new Error("Expected oobChannel=sms"); } }, { name: "Email enrollment", input: { mfaToken: "encrypted-token", authenticatorTypes: ["oob"], oobChannels: ["email"], email: "user@example.com" }, mswResponse: { status: 200, body: { id: "email|dev_abc123", authenticator_type: "oob", oob_channel: "email", oob_code: "email-code-123" } }, expected: (result) => { if (result.authenticatorType !== "oob" || result.oobChannel !== "email") throw new Error("Expected authenticatorType=oob with oobChannel=email"); } }, { name: "Auth0 400 error", input: { mfaToken: "encrypted-token", authenticatorTypes: ["invalid"] }, mswResponse: { status: 400, body: { error: "invalid_request", error_description: "Invalid authenticator type" } }, expectError: (error) => { if (error.code !== "invalid_request") throw new Error("Expected invalid_request"); } } ];