diff --git a/CHANGELOG.md b/CHANGELOG.md index eb062b0..c2df192 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.1.0.0044 — 2026-07-11 + +### Added +- MFA/TOTP check on user settings page — shows setup prompt if no authenticator is configured +- `/api/user/mfa-status` endpoint — checks Authelia for TOTP enrollment status + ## 0.1.0.0043 — 2026-07-11 ### Added diff --git a/VERSION b/VERSION index 40c6030..762552e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0.0043 +0.1.0.0044 diff --git a/main.go b/main.go index 8f86095..b9d42dd 100644 --- a/main.go +++ b/main.go @@ -753,6 +753,37 @@ func publicSettingsHandler(w http.ResponseWriter, r *http.Request) { settings.Company.Name, settings.Company.Subtitle, settings.Company.Logo) } +// --- MFA status check --- + +func checkMFAStatus(w http.ResponseWriter, r *http.Request) { + user := r.Header.Get("Remote-User") + if user == "" { + http.Error(w, "Unauthorized", http.StatusUnauthorized) + return + } + + // Call Authelia API to check TOTP status + token := os.Getenv("AUTHELIA_SECRET") + req, _ := http.NewRequest("GET", "http://authelia:9091/api/user/info", nil) + req.Header.Set("Authorization", "Bearer "+token) + + resp, err := http.DefaultClient.Do(req) + if err != nil { + json.NewEncoder(w).Encode(map[string]bool{"mfa_enabled": false}) + return + } + defer resp.Body.Close() + + var userInfo struct { + TOTP bool `json:"totp"` + } + json.NewDecoder(resp.Body).Decode(&userInfo) + + json.NewEncoder(w).Encode(map[string]bool{ + "mfa_enabled": userInfo.TOTP, + }) +} + // --- Translation system --- var translations = make(map[string]map[string]string) @@ -954,6 +985,8 @@ const settingsHTML = ` .btn { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.5rem 1rem; border-radius: 6px; font-size: 0.88rem; font-weight: 500; cursor: pointer; border: none; } .btn-primary { background: #1a1a2e; color: #fff; } .btn-primary:hover { background: #2d3748; } + .btn-secondary { display: inline-block; background: #1a1a2e; color: #fff; padding: 8px 20px; border-radius: 6px; text-decoration: none; margin-top: 0.5rem; } + .btn-secondary:hover { background: #2d3748; } .actions { display: flex; gap: 0.75rem; align-items: center; margin-top: 1rem; } .saved-msg { color: #48bb78; font-size: 0.9rem; display: none; } @@ -1021,6 +1054,22 @@ const settingsHTML = ` {{t .Lang "saved"}} + +
+ {{if .IsAdmin}}