Skip to content
This repository was archived by the owner on Apr 10, 2026. It is now read-only.
This repository was archived by the owner on Apr 10, 2026. It is now read-only.

feat: Add TOTP two-factor authentication #50

@dajbelshaw

Description

@dajbelshaw

Overview

Add TOTP-based two-factor authentication (RFC 6238) so users can protect their accounts with an authenticator app (Google Authenticator, Authy, 1Password, etc.).

No SMS or third-party auth services — TOTP is computed client-side by the authenticator app and verified server-side with a shared secret.

Scope

This is the tracking issue. Sub-issues cover each layer:

Design decisions

MFA pending state: After a successful password check, if 2FA is enabled the server returns 202 Accepted with a short-lived signed JWT (type: 'mfa_pending', 5 min expiry, not usable as an access token). The client then prompts for a TOTP code and posts it to /api/auth/totp. This avoids storing any per-login state in the DB.

Backup codes: 8 single-use codes generated at setup time, shown once, stored as bcrypt hashes in the DB. Usable in place of a TOTP code at both the login step and the disable-2FA step.

Password reset: Disables 2FA and clears totp_secret when key_salt is rotated (password reset). A user who has lost both their device and backup codes can recover via password reset.

Libraries (server-side):

  • otpauth — TOTP generation + verification, pure JS, no native deps
  • qrcode — generate QR code as data URL for setup UI

Out of scope (for now)

  • SMS-based 2FA
  • WebAuthn / hardware keys
  • Per-device trusted sessions ("remember this device for 30 days")

Metadata

Metadata

Assignees

No one assigned

    Labels

    authAuthentication & sessionsenhancementNew feature or requestsecuritySecurity concern

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions