From 0b20a0d027f9207740584cbb400e2af97c7a68cd Mon Sep 17 00:00:00 2001 From: hex Date: Tue, 6 May 2025 08:39:19 +0200 Subject: [PATCH] feat: auth middleware + /me endpoint --- internal/api/routes/auth.go | 5 +++++ internal/auth/jwt.go | 30 ++++++++++++++++++++++++++++++ internal/auth/types.go | 10 +++++----- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/internal/api/routes/auth.go b/internal/api/routes/auth.go index 709eae4..a6a634f 100644 --- a/internal/api/routes/auth.go +++ b/internal/api/routes/auth.go @@ -33,4 +33,9 @@ func RegisterAuthRoutes(cfg *types.StereoConfig, api *gin.RouterGroup) { c.String(http.StatusOK, jwt) }) + + api.GET("/auth/me", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) { + claims, _ := c.Get("claims") + c.JSON(http.StatusOK, claims) + }) } diff --git a/internal/auth/jwt.go b/internal/auth/jwt.go index d2ee5d7..6a01712 100644 --- a/internal/auth/jwt.go +++ b/internal/auth/jwt.go @@ -2,7 +2,10 @@ package auth import ( "fmt" + "net/http" + "strings" + "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) @@ -16,6 +19,33 @@ func GenerateJWT(key string, user User, expiryTimestamp uint64) (string, error) return token.SignedString([]byte(key)) } +func invalidAuth(c *gin.Context) { + c.String(http.StatusUnauthorized, "Unauthorized.") + c.Abort() +} + +func JwtMiddleware(secret string) gin.HandlerFunc { + return func(c *gin.Context) { + jwtSplit := strings.Split(c.GetHeader("Authorization"), " ") + + if jwtSplit[0] != "Bearer" { + invalidAuth(c) + return + } + + claims, err := ValidateJWT(jwtSplit[1], secret) + + if err != nil { + invalidAuth(c) + return + } + + c.Set("claims", claims) + + c.Next() + } +} + func ValidateJWT(jwtString, key string) (jwt.MapClaims, error) { token, err := jwt.Parse(jwtString, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { diff --git a/internal/auth/types.go b/internal/auth/types.go index 2b61913..a83b941 100644 --- a/internal/auth/types.go +++ b/internal/auth/types.go @@ -11,11 +11,11 @@ type TokenResponse struct { } type User struct { - ID string `json:"id" gorm:"primaryKey"` - Username string `json:"username"` - Blacklisted bool - Email string `json:"email"` - CreatedAt time.Time + ID string `json:"id" gorm:"primaryKey"` + Username string `json:"username"` + Blacklisted bool `json:"blacklisted"` + Email string `json:"email"` + CreatedAt time.Time `json:"created_at"` } type AvatarDecorationData struct {