Second factors and recovery Required
Tell people about every change to their sign-in
Notify every verified address within 5 minutes of a security change. That means an added or removed factor, a used recovery code, a new email or password, or a new device.
How to do it
NIST SP 800-63B-4 section 4.6 asks for these notices, through a channel other than the one that changed. Say what happened, when, and on which device and approximate place. Include no secret and no link that signs in. Link to settings and to a "This wasn't me" page. That page ends every session and holds sensitive changes until the person proves who they are. Send these notices even to people who turned marketing email off.
Example
const NOTIFY_ON = new Set(['factor.added', 'factor.removed', 'recovery.used', 'email.changed', 'password.changed', 'session.new_device']);
export async function recordSecurityEvent(event) {
await securityLog.append(event);
if (NOTIFY_ON.has(event.type)) await mailer.sendSecurityNotice(event.userId, event);
}
NOTIFY_ON = {"factor.added", "factor.removed", "recovery.used", "email.changed", "password.changed", "session.new_device"}
def record_security_event(event) -> None:
security_log.append(event)
if event.type in NOTIFY_ON:
mailer.send_security_notice(event.user_id, event)
How to check
On staging, add and remove a factor, use a recovery code and sign in from a new browser. Each sends one notice within 5 minutes, and none holds a sign-in link.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers accounts playbook: https://agentwranglers.se/playbook/accounts/second-factors-and-recovery/security-notifications.