---
title: "Offer second factors from strongest to · Agent Wranglers"
url: https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/second-factor-choice
updated: 2026-10-21
licence: "All rights reserved"
---

# Offer second factors from strongest to weakest

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- Level: Required
- Chapter: [Second factors and recovery](https://agentwranglers.se/playbook/accounts/second-factors-and-recovery)
- Rule: Offer passkeys and security keys first, then authenticator apps. Offer push approval only when the person types a number from the sign-in screen. SMS and email codes are fallbacks, never an admin's only factor.
- How: NIST SP 800-63B-4 counts only cryptographic authenticators, such as passkeys and security keys, as phishing-resistant. It says a service at its AAL2 level shall offer at least one such option. Codes typed by hand, from an app, a text or a call, can be phished. Set up authenticator apps as [confirm a second factor before turning it on](https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/mfa-enrolment) says. NIST no longer accepts push prompts that only ask to approve, because of fatigue attacks. The person must type the number shown on the sign-in screen, and picking it from a list is not enough. NIST calls SMS and voice codes a restricted authenticator. If you offer them, also offer a factor that is not restricted, and tell people the risks of SIM swaps. Email codes are never a second factor under NIST. Admins follow [offer two-factor login](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/two-factor-login).
- Check: The settings page lists factor types in the order passkey, security key, authenticator app, then text message. A test admin account with only SMS is held at the setup page. A push prompt without a typed number cannot be approved.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
const FACTORS_BY_STRENGTH = ['passkey', 'security_key', 'totp_app', 'push_number_match', 'sms'];
const PHISHING_RESISTANT = new Set(['passkey', 'security_key']);
const RESTRICTED = new Set(['sms']);

export function factorsOfferedTo(user) {
  return user.isAdmin ? FACTORS_BY_STRENGTH.filter(type => PHISHING_RESISTANT.has(type)) : FACTORS_BY_STRENGTH;
}

export const mayRemoveFactor = (user, removedId) =>
  user.factors.some(factor => factor.id !== removedId && !RESTRICTED.has(factor.type));
```

### Python example

```python
FACTORS_BY_STRENGTH = ["passkey", "security_key", "totp_app", "push_number_match", "sms"]
PHISHING_RESISTANT = {"passkey", "security_key"}
RESTRICTED = {"sms"}

def factors_offered_to(user) -> list[str]:
    if user.is_admin:
        return [kind for kind in FACTORS_BY_STRENGTH if kind in PHISHING_RESISTANT]
    return FACTORS_BY_STRENGTH

def may_remove_factor(user, removed_id: str) -> bool:
    return any(f.id != removed_id and f.type not in RESTRICTED for f in user.factors)
```

## Sources

- [NIST SP 800-63B-4 on phishing resistance](https://pages.nist.gov/800-63-4/sp800-63b.html#verifimpers)
- [NIST SP 800-63B-4 on restricted authenticators](https://pages.nist.gov/800-63-4/sp800-63b.html#restricted)
- [NIST SP 800-63B-4 on out-of-band authenticators](https://pages.nist.gov/800-63-4/sp800-63b.html#out-of-band)

## Related rules

- [Confirm a second factor before turning it on](https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/mfa-enrolment) in Accounts
- [Offer two-factor login](https://agentwranglers.se/playbook/adversarial-testing/access-and-accounts/two-factor-login) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/second-factor-choice">Agent Wranglers accounts playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Confirm a second factor before turning it on](https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/mfa-enrolment)
- [Second factors and recovery](https://agentwranglers.se/playbook/accounts/second-factors-and-recovery)
