package routes import ( "bssapp-backend/auth" "bssapp-backend/internal/security" "bssapp-backend/repository" "database/sql" "encoding/json" "net/http" "time" ) func setRefreshCookie(w http.ResponseWriter, plain string, exp time.Time) { http.SetCookie(w, &http.Cookie{ Name: "mk_refresh", Value: plain, Path: "/", Expires: exp, HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: false, // prod: true }) } func AuthRefreshHandler(db *sql.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") // 1) refresh cookie c, err := r.Cookie("mk_refresh") if err != nil || c.Value == "" { http.Error(w, "refresh token missing", http.StatusUnauthorized) return } hash := security.HashRefreshToken(c.Value) rtRepo := repository.NewRefreshTokenRepository(db) // 2) validate + consume mkUserID, err := rtRepo.ConsumeValid(hash) if err != nil { http.Error(w, "refresh token invalid", http.StatusUnauthorized) return } // 3) rotate newPlain, newHash, err := security.GenerateRefreshToken() if err != nil { http.Error(w, "refresh gen failed", http.StatusInternalServerError) return } refreshExp := time.Now().Add(14 * 24 * time.Hour) if err := rtRepo.IssueRefreshToken(mkUserID, newHash, refreshExp); err != nil { http.Error(w, "refresh store failed", http.StatusInternalServerError) return } setRefreshCookie(w, newPlain, refreshExp) // 4) mk user reload mkRepo := repository.NewMkUserRepository(db) mkUser, err := mkRepo.GetByID(mkUserID) if err != nil || !mkUser.IsActive { http.Error(w, "user invalid", http.StatusUnauthorized) return } if mkUser.ForcePasswordChange { http.Error(w, "password change required", http.StatusForbidden) return } // 5) new access token claims := auth.BuildClaimsFromUser(mkUser, 15*time.Minute) token, err := auth.GenerateToken( claims, mkUser.Username, mkUser.ForcePasswordChange, ) if err != nil { http.Error(w, "access token gen failed", http.StatusInternalServerError) return } // 6) response _ = json.NewEncoder(w).Encode(map[string]any{ "success": true, "token": token, }) } }