fix(reset): validate token on GET and surface errors on POST
All checks were successful
continuous-integration/drone/push Build is passing

- Verify reset token before showing the form (GET), redirecting with
  an error immediately if the token is invalid or expired
- Add peekResetToken to check token validity non-destructively
- Fix POST form action to include query params so the URL check doesn't
  silently redirect to /lost before processing errors
- Update page title and subtitle to reflect the reset step

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-03-12 12:11:45 +07:00
commit 71805cf65c
3 changed files with 19 additions and 3 deletions

10
lost.go
View file

@ -49,6 +49,16 @@ func storeResetToken(token string, dn string) {
}
}
func peekResetToken(token string) bool {
resetTokenStore.mu.Lock()
defer resetTokenStore.mu.Unlock()
entry, ok := resetTokenStore.tokens[token]
if !ok || time.Now().After(entry.expiresAt) {
return false
}
return true
}
func consumeResetToken(token string) (string, bool) {
resetTokenStore.mu.Lock()
defer resetTokenStore.mu.Unlock()

View file

@ -22,6 +22,12 @@ func resetPassword(w http.ResponseWriter, r *http.Request) {
}
if r.Method != "POST" {
if !peekResetToken(r.URL.Query().Get("t")) {
displayTmplError(w, http.StatusGone, "lost.html", map[string]any{
"error": "Token invalid or expired, please retry the lost password procedure. Tokens expire after 1 hour.",
})
return
}
csrfToken, err := setCSRFToken(w)
if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError)

View file

@ -1,8 +1,8 @@
{{template "header" .}}
<h1 class="page-title">Forgot your password?</h1>
<p class="page-subtitle">Define a new one!</p>
<h1 class="page-title">Define your new password</h1>
<p class="page-subtitle">Choose a strong password to secure your account.</p>
<form method="post" action="reset">
<form method="post" action="reset?l={{ .login }}&t={{ .token }}">
{{if .error}}<div class="alert alert-error" role="alert">{{.error}}</div>{{end}}
<input type="hidden" name="csrf_token" value="{{ .csrf_token }}">
<div class="form-field">