(FIX): Fixed the JWT handling, no need to fail.

We don't need to fail, but we do need a way to know when a user is
logged in. The new domain(server) function IsLoggedIn will do just that!
This commit is contained in:
Hayden Hargreaves 2025-06-15 20:34:03 -07:00
parent 4a0eed2fc6
commit 5e07383ab0
3 changed files with 37 additions and 38 deletions

View File

@ -1,10 +1,7 @@
package server package server
import ( import (
"errors"
"fmt" "fmt"
"log"
"net/http"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
@ -20,38 +17,20 @@ func DepedencyInjectionMiddleware(deps *domain.InjectedDependencies) gin.Handler
} }
} }
// JwtAuthMiddleWare handles collection the JWT from the browser's cookies and setting the
// appropriate data. If the data is not found, this middleware will do effectively nothing, by not
// setting any values. Protected routes can use this lack of a value as a sign that the user is not
// logged in and direct the user to login.
func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc { func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc {
return func(ctx *gin.Context) { return func(ctx *gin.Context) {
// NOTE: Option One: From auth header // JWT cookie not found
//
// authHeader := ctx.GetHeader("Authorization")
//
// if authHeader == "" {
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization required."})
// ctx.Abort()
// return
// }
//
// parts := strings.SplitN(authHeader, " ", 2)
// if !(len(parts) == 2 && parts[0] == "Bearer") {
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header must be Bearer token"})
// ctx.Abort()
// return
// }
//
// tokenString := parts[1]
// TODO: How are we handling faliure?
tokenString, err := ctx.Cookie("jwt_token") tokenString, err := ctx.Cookie("jwt_token")
if err != nil { if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "JWT token cookie not found"}) ctx.Next()
ctx.Abort()
return return
} }
claims := &domain.JwtClaims{} claims := &domain.JwtClaims{}
// token, err := jwt.ParseWithClaims(tokenString, claims, )
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 {
@ -60,22 +39,29 @@ func JwtAuthMiddleWare(jwtSecretKey []byte) gin.HandlerFunc {
return jwtSecretKey, nil return jwtSecretKey, nil
}) })
// Error occurred when parsing
if err != nil { if err != nil {
if errors.Is(err, jwt.ErrSignatureInvalid) { ctx.Next()
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"})
} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or not yet valid"})
} else {
log.Printf("JWT parsing error: %v", err)
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
}
ctx.Abort()
return return
} }
// NOTE: If we need deeper error handling
// if err != nil {
// if errors.Is(err, jwt.ErrSignatureInvalid) {
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token signature"})
// } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Token expired or not yet valid"})
// } else {
// log.Printf("JWT parsing error: %v", err)
// ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
// }
// ctx.Abort()
// return
// }
// Token is invalid
if !token.Valid { if !token.Valid {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"}) ctx.Next()
ctx.Abort()
return return
} }

View File

@ -135,6 +135,11 @@ func (s *Server) Setup() *Server {
// API router endpoints // API router endpoints
router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) }) router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
router_api.GET("/tmp", func(ctx *gin.Context) { router_api.GET("/tmp", func(ctx *gin.Context) {
if !domain.IsLoggedIn(ctx) {
ctx.JSON(200, gin.H{"error": "User is not logged in. Please login to continue"})
return
}
userId := ctx.MustGet("userId").(int) userId := ctx.MustGet("userId").(int)
userEmail := ctx.MustGet("userEmail").(string) userEmail := ctx.MustGet("userEmail").(string)

View File

@ -1,6 +1,7 @@
package domain package domain
import ( import (
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth" domainAuth "github.com/haydenhargreaves/Potion/internal/domain/auth"
domainUser "github.com/haydenhargreaves/Potion/internal/domain/user" domainUser "github.com/haydenhargreaves/Potion/internal/domain/user"
@ -16,3 +17,10 @@ type JwtClaims struct {
Email string `json:"email"` Email string `json:"email"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
// IsLoggedIn checks the cookies in a request and returns whether the user is logged in.
func IsLoggedIn(ctx *gin.Context) bool {
_, id := ctx.Get("userId")
_, email := ctx.Get("userEmail")
return id && email
}