MCPcopy Index your code
hub / github.com/PatchMon/PatchMon / VerifyTfa

Method VerifyTfa

server-source-code/internal/handler/auth.go:328–400  ·  view source on GitHub ↗

VerifyTfa handles POST /auth/verify-tfa.

(w http.ResponseWriter, r *http.Request)

Source from the content-addressed store, hash-verified

326
327// VerifyTfa handles POST /auth/verify-tfa.
328func (h *AuthHandler) VerifyTfa(w http.ResponseWriter, r *http.Request) {
329 var req VerifyTfaRequest
330 if err := decodeJSON(r, &req); err != nil {
331 Error(w, http.StatusBadRequest, "Invalid request body")
332 return
333 }
334 req.Token = strings.ToUpper(strings.TrimSpace(req.Token))
335 if req.Username == "" || len(req.Token) != 6 {
336 Error(w, http.StatusBadRequest, "Username and 6-character token required")
337 return
338 }
339 if !util.TokenRegex.MatchString(req.Token) {
340 Error(w, http.StatusBadRequest, "Token must be 6 alphanumeric characters")
341 return
342 }
343
344 user, err := h.users.GetByUsernameOrEmail(r.Context(), req.Username)
345 if err != nil || user == nil || !user.IsActive || !user.TfaEnabled || user.TfaSecret == nil {
346 Error(w, http.StatusUnauthorized, "Invalid credentials or TFA not enabled")
347 return
348 }
349
350 if h.tfaLockout != nil {
351 locked, _ := h.tfaLockout.IsTFALocked(r.Context(), user.ID)
352 if locked {
353 Error(w, http.StatusTooManyRequests, "Too many failed TFA attempts. Please try again later.")
354 return
355 }
356 }
357
358 secret := strings.TrimSpace(*user.TfaSecret)
359 verified := util.VerifyTOTP(secret, req.Token, util.TOTPWindow)
360
361 if !verified {
362 hashed := util.ParseBackupCodesJSON(user.TfaBackupCodes)
363 if len(hashed) > 0 {
364 valid, idx := util.VerifyBackupCode(req.Token, hashed)
365 if valid {
366 verified = true
367 hashed = append(hashed[:idx], hashed[idx+1:]...)
368 updated := util.EncodeBackupCodesJSON(hashed)
369 _ = h.users.UpdateTfaBackupCodes(r.Context(), user.ID, &updated)
370 }
371 }
372 }
373
374 if !verified {
375 if h.tfaLockout != nil {
376 attempts, locked := h.tfaLockout.RecordFailedAttempt(r.Context(), user.ID)
377 if locked {
378 Error(w, http.StatusTooManyRequests, "Too many failed TFA attempts. Please try again later.")
379 return
380 }
381 remaining := h.getMaxTfaAttempts() - attempts
382 if remaining < 0 {
383 remaining = 0
384 }
385 JSON(w, http.StatusUnauthorized, map[string]interface{}{

Callers

nothing calls this directly

Calls 10

getMaxTfaAttemptsMethod · 0.95
completeLoginMethod · 0.95
decodeJSONFunction · 0.85
ErrorFunction · 0.85
GetByUsernameOrEmailMethod · 0.80
IsTFALockedMethod · 0.80
JSONFunction · 0.70
UpdateTfaBackupCodesMethod · 0.65
RecordFailedAttemptMethod · 0.45
ClearFailedAttemptsMethod · 0.45

Tested by

no test coverage detected