package api import ( "context" "encoding/json" "log" "net/http" "geniusrun/backend/internal/garmin" ) 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" } } // recordAuthResult updates the in-memory auth status/message for userID, // and -- on a successful authentication -- persists that this account has // connected to Garmin at least once (store.MarkGarminConnected), which is // what the login gate actually checks (the in-memory auth status resets on // every backend restart; this doesn't). func (s *Server) recordAuthResult(ctx context.Context, userID int64, res garmin.AuthResult) { s.mu.Lock() s.userAuthStatus[userID] = res.Status s.userAuthMessage[userID] = res.Message s.mu.Unlock() if res.Status == garmin.AuthSuccess { if err := s.DB.MarkGarminConnected(ctx, userID); err != nil { log.Printf("api: mark garmin connected for user %d: %v", userID, err) } } } func (s *Server) handleAuthLogin(w http.ResponseWriter, r *http.Request) { 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()) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } s.recordAuthResult(r.Context(), userID, res) writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message}) } func (s *Server) handleAuthMFA(w http.ResponseWriter, r *http.Request) { userID := userIDFromContext(r.Context()) 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 } 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) if err != nil { writeError(w, http.StatusBadGateway, err.Error()) return } s.recordAuthResult(r.Context(), userID, res) writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(res.Status), Message: res.Message}) } func (s *Server) handleAuthStatus(w http.ResponseWriter, r *http.Request) { userID := userIDFromContext(r.Context()) s.mu.Lock() status, msg := s.userAuthStatus[userID], s.userAuthMessage[userID] s.mu.Unlock() writeJSON(w, http.StatusOK, authResponse{Status: authStatusString(status), Message: msg}) }