diff --git a/internal/app/server/authentication.go b/internal/app/server/authentication.go index 1e03839..f3cc4e8 100644 --- a/internal/app/server/authentication.go +++ b/internal/app/server/authentication.go @@ -24,4 +24,16 @@ func (s *Server) withAuthenticatedUser(ctx *gin.Context, handler AuthenticatedFu handler(ctx, user) } -// TODO: Create a function to use for methods that CAN use a user, but sometimes don't. +// getUserId retrieves the userId from the context and returns a pointer to it. A nil +// pointer can be returned and will if the userId does not exist. +func getUserId(ctx *gin.Context) *int { + userIdAny, exists := ctx.Get("userId") + if !exists { + return nil + } + userIdInt, ok := userIdAny.(int) + if !ok { + return nil + } + return &userIdInt +} diff --git a/internal/app/server/middleware_v2.go b/internal/app/server/middleware_v2.go index 57c9375..fbdaa5f 100644 --- a/internal/app/server/middleware_v2.go +++ b/internal/app/server/middleware_v2.go @@ -9,7 +9,7 @@ import ( domain "github.com/haydenhargreaves/Potion/internal/domain/server" ) -// JwtAuthMiddlewareV2 is responsible to protecting routes. Anything that may go wrong +// JwtAuthMiddlewareV2 is responsible for protecting routes. Anything that may go wrong // will be returned via JSON with a 'message' field and a 401 error code. When this // middleware is successful, it will set the 'userId' and 'userEmail' fields and pass // to the next function in the chain. @@ -63,3 +63,36 @@ func JwtAuthMiddlewareV2(jwtSecretKey []byte) gin.HandlerFunc { ctx.Next() } } + +// JwtOptionalAuthMiddlewareV2 is responsible for collecting user data for routes where +// authentication is optional. Meaning: if the use is not logged in, this function does +// not fail or return, it simply does nothing. But if the user is logged in, then the +// 'userId' and 'userEmail' context values are set. +// +// e.g., `userIdAny, exists := ctx.Get("userId")` +func JwtOptionalAuthMiddlewareV2(jwtSecretKey []byte) gin.HandlerFunc { + return func(ctx *gin.Context) { + tokenString, err := ctx.Cookie("jwt_token") + if err != nil || tokenString == "" { + // No cookie found: not authenticated, but allow access + ctx.Next() + return + } + + claims := &domain.JwtClaims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return jwtSecretKey, nil + }) + + if err == nil && token.Valid { + // Set user info in context if token is valid + ctx.Set("userId", claims.UserId) + ctx.Set("userEmail", claims.Email) + } + // Otherwise, just continue (user is unauthenticated) + ctx.Next() + } +} diff --git a/internal/app/server/recipe_handler_v2.go b/internal/app/server/recipe_handler_v2.go index d38e553..7331435 100644 --- a/internal/app/server/recipe_handler_v2.go +++ b/internal/app/server/recipe_handler_v2.go @@ -14,10 +14,8 @@ import ( // Until auth is reimplemented, there is no way to determine what user is making the // call. func (s *Server) GetRecipeOfTheWeekHandlerV2(ctx *gin.Context) { - // BUG: This needs to be different, not hard coded - userId := 1 - - recipe, err := s.deps.RecipeService.GetRecipeOfTheWeek(&userId) + userId := getUserId(ctx) + recipe, err := s.deps.RecipeService.GetRecipeOfTheWeek(userId) if err != nil { ctx.JSON(http.StatusBadRequest, gin.H{ diff --git a/internal/app/server/server.go b/internal/app/server/server.go index 94cdf6f..dea2a97 100644 --- a/internal/app/server/server.go +++ b/internal/app/server/server.go @@ -201,7 +201,7 @@ func (s *Server) Setup() *Server { // ---- VERSION 2 ROUTES ---- // router_api_v2 := router_v2.Group(domain.API) - router_api_v2.GET("/recipe/of-the-week", s.GetRecipeOfTheWeekHandlerV2) + router_api_v2.GET("/recipe/of-the-week", JwtOptionalAuthMiddlewareV2([]byte(cfg.JwtSecret)), s.GetRecipeOfTheWeekHandlerV2) router_api_v2.GET("/auth/login", s.GetGoogleAuthUrlHandlerV2) router_api_v2.GET("/auth/callback", s.GoogleCallbackHandlerV2) router_api_v2.GET("/auth/logout", s.LogoutHandlerV2)