
Auth0 Mfa
Add adaptive Auth0 MFA with Post Login Actions for admins, new devices, geo risk, and sensitive OAuth scopes.
Overview
Auth0 MFA is an agent skill for the Ship phase that implements adaptive multi-factor authentication using Auth0 Post Login Actions.
Install
npx skills add https://github.com/auth0/agent-skills --skill auth0-mfaWhat is this skill?
- Post Login Action snippets for role-based MFA (e.g. admin always stepped up)
- New-device detection path with optional remember-browser behavior
- GeoIP / country mismatch trigger versus stored user_metadata country
- Scope-aware MFA when requested scopes include sensitive operations
- Uses Auth0 api.multifactor.enable with configurable allowRememberBrowser
Adoption & trust: 580 installs on skills.sh; 26 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need MFA that fires only for risky logins or sensitive scopes, but your Auth0 tenant still treats every user the same.
Who is it for?
Indie SaaS on Auth0 that must step up admins and high-risk API scopes without annoying every daily login.
Skip if: Greenfield apps not on Auth0, or teams wanting client-side-only auth with no Actions access.
When should I use this skill?
You are configuring Auth0 MFA with Actions for conditional login or sensitive scope access.
What do I get? / Deliverables
You deploy conditional MFA Action code aligned to roles, devices, location signals, and sensitive scopes in your Auth0 tenant.
- Post Login Action scripts for conditional MFA
- Branching logic for admin, device, geo, and scope triggers
Recommended Skills
Journey fit
How it compares
Auth0 tenant Actions skill—not a generic passwordless or passkeys guide, and not a full SOC2 policy pack.
Common Questions / FAQ
Who is auth0-mfa for?
Solo builders and small teams shipping SaaS or APIs on Auth0 who need conditional MFA in production tenants.
When should I use auth0-mfa?
During Ship security hardening before go-live, or when adding sensitive OAuth scopes that require step-up authentication.
Is auth0-mfa safe to install?
It generates auth policy code you deploy yourself—review the Security Audits panel on this page and test Actions in a non-production tenant first.
SKILL.md
READMESKILL.md - Auth0 Mfa
## Step 4: Adaptive MFA with Actions Use Auth0 Actions to require MFA based on conditions. ### Create Action: Conditional MFA ```javascript // Action: Require MFA for Sensitive Operations // Trigger: Login / Post Login exports.onExecutePostLogin = async (event, api) => { // Always require MFA for admins const roles = event.authorization?.roles || []; if (roles.includes('admin')) { if (event.authentication?.methods?.find(m => m.name === 'mfa')) { return; // MFA already completed } api.multifactor.enable('any', { allowRememberBrowser: false }); return; } // Require MFA for new devices const isNewDevice = !event.authentication?.methods?.find( m => m.name === 'pwd' && m.timestamp ); if (isNewDevice) { api.multifactor.enable('any', { allowRememberBrowser: true }); return; } // Require MFA for suspicious locations const riskAssessment = event.request?.geoip; const userCountry = event.user?.user_metadata?.country; if (riskAssessment?.countryCode !== userCountry) { api.multifactor.enable('any', { allowRememberBrowser: false }); return; } }; ``` ### Create Action: MFA Based on Requested Scopes ```javascript // Action: MFA for Sensitive Scopes // Trigger: Login / Post Login exports.onExecutePostLogin = async (event, api) => { const requestedScopes = event.request?.query?.scope?.split(' ') || []; const sensitiveScopes = ['transfer:funds', 'admin:write', 'delete:users']; const requiresMFA = requestedScopes.some(scope => sensitiveScopes.includes(scope) ); if (requiresMFA) { const hasMFA = event.authentication?.methods?.find(m => m.name === 'mfa'); if (!hasMFA) { api.multifactor.enable('any'); } } }; ``` ### Deploy Action via CLI ```bash # Create the action auth0 actions create \ --name "Conditional MFA" \ --trigger post-login \ --code "$(cat conditional-mfa.js)" # Deploy the action auth0 actions deploy ACTION_ID # Attach to login flow auth0 api patch "actions/triggers/post-login/bindings" --data '{ "bindings": [{"ref": {"type": "action_id", "value": "ACTION_ID"}}] }' ``` --- ## Step 5: MFA Enrollment API For custom enrollment experiences, use the MFA API. ### List User's MFA Enrollments ```bash # Get user's enrolled authenticators curl -X GET "https://YOUR_DOMAIN/api/v2/users/USER_ID/authenticators" \ -H "Authorization: Bearer MGMT_TOKEN" ``` ### Delete an Enrollment ```bash # Remove an authenticator curl -X DELETE "https://YOUR_DOMAIN/api/v2/users/USER_ID/authenticators/AUTHENTICATOR_ID" \ -H "Authorization: Bearer MGMT_TOKEN" ``` ### Trigger Enrollment Email ```bash # Send enrollment email to user curl -X POST "https://YOUR_DOMAIN/api/v2/guardian/enrollments/ticket" \ -H "Authorization: Bearer MGMT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "user_id": "USER_ID", "send_mail": true }' ``` --- ## Common Patterns ### Pattern 1: Remember MFA for 30 Days ```typescript // React: Check MFA age before requiring const requireMFAIfStale = async (maxAgeSeconds = 30 * 24 * 60 * 60) => { const claims = await getIdTokenClaims(); const authTime = claims?.auth_time; if (!authTime) return requireMFA(); const authAge = Math.floor(Date.now() / 1000) - authTime; if (authAge > maxAgeSeconds) { return requireMFA({ maxAge: 0 }); } return hasMFA(); }; ``` ### Pattern 2: MFA Challenge for High-Value Transactions ```typescript // Frontend const transferFunds = async (amount: number) => { // Require MFA for transfers over $1000 if (amount > 1000) { const verified = await requireMFA(); if (!verified) return; } await api.post('/transfer', { amount }); }; // Backend middleware const requireMFAForHighValue = (threshold: number) => { return (req, res, next) => { const amount = req.body?.amount || 0; if (amount > threshold) { const amr = req.auth?.amr || []; if (!amr.includes('mfa')) { return res.status(403).json({ erro