Agent Wranglers

Second factors and recovery Required

Confirm a second factor before turning it on

Turn on a new second factor only after the person proves it works once, and let them give it a name.

How to do it

For an authenticator app, show the QR code and the secret as text, then ask for a current code. Accept one 30-second step either side. Store the last accepted time step and refuse any code from that step or an earlier one, as NIST SP 800-63B-4 section 3.1.4.2 requires. The same check applies at every sign-in. For a passkey or security key, the successful registration is the proof. Keep the factor pending until then, and drop pending ones after 15 minutes. Ask for a name, such as "Work phone", and show it with the date added and last used. Rate limit code entry, as offer two-factor login says.

Example

import { verify } from 'otplib';

const DRIFT_SECONDS = 30;

export async function confirmTotp(db, userId, code) {
  const pending = await db.totp.findPending(userId);
  if (!pending) return false;
  const result = await verify({ secret: pending.secret, token: code, epochTolerance: DRIFT_SECONDS, afterTimeStep: pending.lastTimeStep ?? undefined });
  if (!result.valid) return false;
  await db.totp.activate(pending.id, { lastTimeStep: result.timeStep });
  return true;
}
import hmac
import time
import pyotp

STEP_SECONDS = 30
DRIFT_STEPS = 1

def matching_step(secret: str, code: str, last_step: int | None) -> int | None:
    totp = pyotp.TOTP(secret)
    current = int(time.time()) // STEP_SECONDS
    for step in range(current - DRIFT_STEPS, current + DRIFT_STEPS + 1):
        if (last_step is None or step > last_step) and hmac.compare_digest(totp.at(step * STEP_SECONDS), code):
            return step
    return None

def confirm_totp(db, user_id: str, code: str) -> bool:
    pending = db.totp.find_pending(user_id)
    step = matching_step(pending.secret, code, pending.last_step) if pending else None
    if step is None:
        return False
    db.totp.activate(pending.id, last_step=step)
    return True

How to check

Start authenticator setup and submit a wrong code. Two-factor login stays off. Submit the current code, and it turns on under the given name. At sign-in, the same code a second time is refused.

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/mfa-enrolment.