Add two-factor login (TOTP) with recovery codes

Users can set up an authenticator app from their account page; login
then asks for a code after the password, with one-time recovery codes
as a fallback. Sensitive actions (tokens, user changes, 2FA resets)
need a code from the last 5 minutes. totp.require (none/admins/all)
forces setup, and admins can reset a user's 2FA from the UI, the jarl
CLI or jarld on the server. The dashboard and jarl status flag users
missing 2FA and a shared "admin" account still in use.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
abrown38andClaude Opus 5.5 committed 2026-10-09 14:12:24 +00:00
1 parent b22ffee1bb
commit 8f9cdd0a17
35 files changed
+1965 -43

No files matched your search

+12
View File
@@ -47,6 +47,10 @@ type Config struct {
// reports on (backup.dir and backup.warn_after).
BackupDir string
BackupWarnAfter time.Duration
// TOTPRequire is totp.require (none, admins or all) and TOTPIssuer the
// name authenticator apps show (h_totp.go).
TOTPRequire string
TOTPIssuer string
}
// Server is the HTTP server.
@@ -64,6 +68,8 @@ type Server struct {
oldSecretWarn throttle
// expiryWarn limits warnings about expiring tokens to one a day each.
expiryWarn throttle
// pendingLogins are logins waiting for their TOTP code.
pendingLogins pendingLogins
// nsChecker, when set, replaces the default name server checker
// (tests point it at local DNS servers).
nsChecker *nscheck.Checker
@@ -86,6 +92,12 @@ func New(cfg Config, eng *engine.Engine, log *slog.Logger, web fs.FS) *Server {
if cfg.CookieSecure == "" {
cfg.CookieSecure = "auto"
}
if cfg.TOTPRequire == "" {
cfg.TOTPRequire = "none"
}
if cfg.TOTPIssuer == "" {
cfg.TOTPIssuer = "Jarl"
}
if log == nil {
log = slog.Default()
}