add auth middleware

This commit is contained in:
2026-02-23 16:39:04 -05:00
parent af9c23c521
commit 11eb192e2a
4 changed files with 89 additions and 5 deletions
+39 -2
View File
@@ -10,10 +10,11 @@ import (
type UserHandler struct { type UserHandler struct {
userService *services.UserService userService *services.UserService
authService *services.AuthService
} }
func NewUserHandler(us *services.UserService) *UserHandler { func NewUserHandler(us *services.UserService, as *services.AuthService) *UserHandler {
return &UserHandler{userService: us} return &UserHandler{userService: us, authService: as}
} }
func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) { func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) {
@@ -60,3 +61,39 @@ func (h *UserHandler) Register(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(user)
} }
func (h *UserHandler) Login(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
var req models.LoginRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid request body", http.StatusBadRequest)
return
}
req.Email = strings.ToLower(strings.TrimSpace(req.Email))
user, err := h.userService.GetUserByEmail(req.Email)
if err != nil || user == nil {
http.Error(w, "invalid credentials", http.StatusUnauthorized)
return
}
if !h.userService.ValidatePassword(user, req.Password) {
http.Error(w, "invalid credentials", http.StatusUnauthorized)
return
}
token, err := h.authService.GenerateToken(user.ID, user.Email)
if err != nil {
http.Error(w, "failed to generate token", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(models.LoginResponse{User: *user, Token: token})
}
+8 -2
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"go-weather/config" "go-weather/config"
"go-weather/handlers" "go-weather/handlers"
"go-weather/middleware"
"go-weather/services" "go-weather/services"
"log" "log"
"net/http" "net/http"
@@ -64,16 +65,21 @@ func main() {
databaseService := services.NewDatabaseService(database) databaseService := services.NewDatabaseService(database)
weatherService := services.NewWeatherService(config.APIKey) weatherService := services.NewWeatherService(config.APIKey)
userService := services.NewUserService(database) userService := services.NewUserService(database)
authService := services.NewAuthService(config.JWTSecret)
// Create route handlers // Create route handlers
weatherHandler := handlers.NewWeatherHandler(weatherService, databaseService, config.DefaultCity) weatherHandler := handlers.NewWeatherHandler(weatherService, databaseService, config.DefaultCity)
userHandler := handlers.NewUserHandler(userService) userHandler := handlers.NewUserHandler(userService, authService)
// Start background poller // Start background poller
go startPoller(database, weatherService, config.DefaultCity, 10*time.Minute) go startPoller(database, weatherService, config.DefaultCity, 10*time.Minute)
// Register routes // Register routes
http.HandleFunc("/weather/fetch", weatherHandler.FetchWeather) http.HandleFunc("/login", userHandler.Login)
http.Handle("/weather/fetch", middleware.AuthMiddleware(authService)(
http.HandlerFunc(weatherHandler.FetchWeather)),
)
http.HandleFunc("/weather/history", weatherHandler.GetHistory) http.HandleFunc("/weather/history", weatherHandler.GetHistory)
http.HandleFunc("/health", handlers.Health) http.HandleFunc("/health", handlers.Health)
http.HandleFunc("/latest-temperature", handlers.LatestTemperature) http.HandleFunc("/latest-temperature", handlers.LatestTemperature)
+41
View File
@@ -0,0 +1,41 @@
package middleware
import (
"context"
"go-weather/services"
"net/http"
"strings"
)
type contextKey string
const ClaimsKey contextKey = "claims"
func AuthMiddleware(authService *services.AuthService) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
claims, err := authService.ValidateToken(tokenString)
if err != nil {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), ClaimsKey, claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func GetClaims(r *http.Request) *services.Claims {
if claims, ok := r.Context().Value(ClaimsKey).(*services.Claims); ok {
return claims
}
return nil
}
+1 -1
View File
@@ -36,7 +36,7 @@ func (s *AuthService) GenerateToken(userID int, email string) (string, error) {
} }
func (s *AuthService) ValidateToken(tokenString string) (*Claims, error) { func (s *AuthService) ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims, func(token *jwt.Token) (interface{}, error) { token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method") return nil, errors.New("unexpected signing method")
} }