2026-07-17 18:33:06 +02:00
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"net/http"
|
|
|
|
|
|
2026-07-24 21:08:07 +02:00
|
|
|
"geniusrun/backend/internal/garmin"
|
2026-07-17 18:33:06 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type authResponse struct {
|
|
|
|
|
Status string `json:"status"` // "authenticated" | "mfa_required" | "failed"
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func authStatusString(s garmin.AuthStatus) string {
|
|
|
|
|
switch s {
|
|
|
|
|
case garmin.AuthSuccess:
|
|
|
|
|
return "authenticated"
|
|
|
|
|
case garmin.AuthMFARequired:
|
|
|
|
|
return "mfa_required"
|
|
|
|
|
case garmin.AuthFailed:
|
|
|
|
|
return "failed"
|
|
|
|
|
default:
|
|
|
|
|
return "unknown"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 18:16:06 +02:00
|
|
|
func (s *Server) recordAuthResult(userID int64, res garmin.AuthResult) {
|
2026-07-17 18:33:06 +02:00
|
|
|
s.mu.Lock()
|
2026-07-25 18:16:06 +02:00
|
|
|
s.userAuthStatus[userID] = res.Status
|
|
|
|
|
s.userAuthMessage[userID] = res.Message
|
2026-07-17 18:33:06 +02:00
|
|
|
s.mu.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) {
|
2026-07-25 18:16:06 +02:00
|
|
|
userID := userIDFromContext(r.Context())
|
|
|
|
|
client, err := s.garminFor(r.Context(), userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res, err := client.Authenticate(r.Context())
|
2026-07-17 18:33:06 +02:00
|
|
|
if err != nil {
|
|
|
|
|
writeError(w, http.StatusBadGateway, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-07-25 18:16:06 +02:00
|
|
|
s.recordAuthResult(userID, res)
|
2026-07-17 18:33:06 +02:00
|
|
|
writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleAuthMFA(w http.ResponseWriter, r *http.Request) {
|
2026-07-25 18:16:06 +02:00
|
|
|
userID := userIDFromContext(r.Context())
|
2026-07-17 18:33:06 +02:00
|
|
|
var body struct {
|
|
|
|
|
Code string `json:"code"`
|
|
|
|
|
}
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
|
|
|
writeError(w, http.StatusBadRequest, "invalid request body")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if body.Code == "" {
|
|
|
|
|
writeError(w, http.StatusBadRequest, "code is required")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 18:16:06 +02:00
|
|
|
client, err := s.garminFor(r.Context(), userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeError(w, http.StatusInternalServerError, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
res, err := client.CompleteMFA(r.Context(), body.Code)
|
2026-07-17 18:33:06 +02:00
|
|
|
if err != nil {
|
|
|
|
|
writeError(w, http.StatusBadGateway, err.Error())
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-07-25 18:16:06 +02:00
|
|
|
s.recordAuthResult(userID, res)
|
2026-07-17 18:33:06 +02:00
|
|
|
writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Server) handleAuthStatus(w http.ResponseWriter, r *http.Request) {
|
2026-07-25 18:16:06 +02:00
|
|
|
userID := userIDFromContext(r.Context())
|
2026-07-17 18:33:06 +02:00
|
|
|
s.mu.Lock()
|
2026-07-25 18:16:06 +02:00
|
|
|
status, msg := s.userAuthStatus[userID], s.userAuthMessage[userID]
|
2026-07-17 18:33:06 +02:00
|
|
|
s.mu.Unlock()
|
|
|
|
|
writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(status), Message: msg})
|
|
|
|
|
}
|