2 Commits

Author SHA1 Message Date
Fran Jurmanović
edad65d6a9 generate open token using normal token
All checks were successful
Release and Deploy / build (push) Successful in 3m1s
Release and Deploy / deploy (push) Successful in 23s
2025-08-17 12:46:37 +02:00
Fran Jurmanović
486c972bba open token authentication
All checks were successful
Release and Deploy / build (push) Successful in 3m51s
Release and Deploy / deploy (push) Successful in 28s
2025-08-17 12:15:39 +02:00
7 changed files with 34 additions and 19 deletions

View File

@@ -34,7 +34,7 @@ func NewMembershipController(service *service.MembershipService, auth *middlewar
} }
routeGroups.Auth.Post("/login", mc.Login) routeGroups.Auth.Post("/login", mc.Login)
routeGroups.Auth.Post("/open-token", mc.GenerateOpenToken) routeGroups.Auth.Post("/open-token", mc.auth.Authenticate, mc.GenerateOpenToken)
usersGroup := routeGroups.Membership usersGroup := routeGroups.Membership
usersGroup.Use(mc.auth.Authenticate) usersGroup.Use(mc.auth.Authenticate)

View File

@@ -52,20 +52,28 @@ func NewAuthMiddleware(ms *service.MembershipService, cache *cache.InMemoryCache
// Authenticate is a middleware for JWT authentication with enhanced security. // Authenticate is a middleware for JWT authentication with enhanced security.
func (m *AuthMiddleware) AuthenticateOpen(ctx *fiber.Ctx) error { func (m *AuthMiddleware) AuthenticateOpen(ctx *fiber.Ctx) error {
return m.AuthenticateWithHandler(m.openJWTHandler.JWTHandler, ctx) return m.AuthenticateWithHandler(m.openJWTHandler.JWTHandler, true, ctx)
} }
// Authenticate is a middleware for JWT authentication with enhanced security. // Authenticate is a middleware for JWT authentication with enhanced security.
func (m *AuthMiddleware) Authenticate(ctx *fiber.Ctx) error { func (m *AuthMiddleware) Authenticate(ctx *fiber.Ctx) error {
return m.AuthenticateWithHandler(m.jwtHandler, ctx) return m.AuthenticateWithHandler(m.jwtHandler, false, ctx)
} }
func (m *AuthMiddleware) AuthenticateWithHandler(jwtHandler *jwt.JWTHandler, ctx *fiber.Ctx) error { func (m *AuthMiddleware) AuthenticateWithHandler(jwtHandler *jwt.JWTHandler, isOpenToken bool, ctx *fiber.Ctx) error {
// Log authentication attempt // Log authentication attempt
ip := ctx.IP() ip := ctx.IP()
userAgent := ctx.Get("User-Agent") userAgent := ctx.Get("User-Agent")
authHeader := ctx.Get("Authorization") authHeader := ctx.Get("Authorization")
if jwtHandler.IsOpenToken && !isOpenToken {
logging.Error("Authentication failed: attempting to authenticate with open token")
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Wrong token type used",
})
}
if authHeader == "" { if authHeader == "" {
logging.Error("Authentication failed: missing Authorization header from IP %s", ip) logging.Error("Authentication failed: missing Authorization header from IP %s", ip)
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
@@ -98,6 +106,13 @@ func (m *AuthMiddleware) AuthenticateWithHandler(jwtHandler *jwt.JWTHandler, ctx
}) })
} }
if !jwtHandler.IsOpenToken && claims.IsOpenToken {
logging.Error("Authentication failed: attempting to authenticate with open token")
return ctx.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"error": "Wrong token type used",
})
}
// Additional security: validate user ID format // Additional security: validate user ID format
if claims.UserID == "" || len(claims.UserID) < 10 { if claims.UserID == "" || len(claims.UserID) < 10 {
logging.Error("Authentication failed: invalid user ID in token from IP %s", ip) logging.Error("Authentication failed: invalid user ID in token from IP %s", ip)

View File

@@ -63,16 +63,11 @@ func (s *MembershipService) Login(ctx context.Context, username, password string
return "", err return "", err
} }
return s.jwtHandler.GenerateToken(user) return s.jwtHandler.GenerateToken(user.ID.String())
} }
func (s *MembershipService) GenerateOpenToken(ctx context.Context, userId string) (string, error) { func (s *MembershipService) GenerateOpenToken(ctx context.Context, userId string) (string, error) {
user, err := s.repo.GetByID(ctx, userId) return s.openJwtHandler.GenerateToken(userId)
if err != nil {
return "", err
}
return s.openJwtHandler.GenerateToken(user)
} }
// CreateUser creates a new user. // CreateUser creates a new user.

View File

@@ -8,7 +8,7 @@ import (
) )
var ( var (
Version = "0.10.5" Version = "0.10.6"
Prefix = "v1" Prefix = "v1"
Secret string Secret string
SecretCode string SecretCode string

View File

@@ -13,12 +13,14 @@ import (
// Claims represents the JWT claims. // Claims represents the JWT claims.
type Claims struct { type Claims struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
IsOpenToken bool `json:"is_open_token"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
type JWTHandler struct { type JWTHandler struct {
SecretKey []byte SecretKey []byte
IsOpenToken bool
} }
type OpenJWTHandler struct { type OpenJWTHandler struct {
@@ -28,6 +30,7 @@ type OpenJWTHandler struct {
// NewJWTHandler creates a new JWTHandler instance with the provided secret key. // NewJWTHandler creates a new JWTHandler instance with the provided secret key.
func NewOpenJWTHandler(jwtSecret string) *OpenJWTHandler { func NewOpenJWTHandler(jwtSecret string) *OpenJWTHandler {
jwtHandler := NewJWTHandler(jwtSecret) jwtHandler := NewJWTHandler(jwtSecret)
jwtHandler.IsOpenToken = true
return &OpenJWTHandler{ return &OpenJWTHandler{
JWTHandler: jwtHandler, JWTHandler: jwtHandler,
} }
@@ -68,13 +71,14 @@ func (jh *JWTHandler) GenerateSecretKey() string {
} }
// GenerateToken generates a new JWT for a given user. // GenerateToken generates a new JWT for a given user.
func (jh *JWTHandler) GenerateToken(user *model.User) (string, error) { func (jh *JWTHandler) GenerateToken(userId string) (string, error) {
expirationTime := time.Now().Add(24 * time.Hour) expirationTime := time.Now().Add(24 * time.Hour)
claims := &Claims{ claims := &Claims{
UserID: user.ID.String(), UserID: userId,
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime), ExpiresAt: jwt.NewNumericDate(expirationTime),
}, },
IsOpenToken: jh.IsOpenToken,
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
@@ -88,6 +92,7 @@ func (jh *JWTHandler) GenerateTokenWithExpiry(user *model.User, expiry time.Time
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime), ExpiresAt: jwt.NewNumericDate(expirationTime),
}, },
IsOpenToken: jh.IsOpenToken,
} }
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

View File

@@ -28,7 +28,7 @@ func GenerateTestToken() (string, error) {
jwtHandler := jwt.NewJWTHandler(testSecret) jwtHandler := jwt.NewJWTHandler(testSecret)
// Generate JWT token // Generate JWT token
token, err := jwtHandler.GenerateToken(user) token, err := jwtHandler.GenerateToken(user.ID.String())
if err != nil { if err != nil {
return "", fmt.Errorf("failed to generate test token: %w", err) return "", fmt.Errorf("failed to generate test token: %w", err)
} }

View File

@@ -26,7 +26,7 @@ func TestJWT_GenerateAndValidateToken(t *testing.T) {
} }
// Test JWT generation // Test JWT generation
token, err := jwtHandler.GenerateToken(user) token, err := jwtHandler.GenerateToken(user.ID.String())
tests.AssertNoError(t, err) tests.AssertNoError(t, err)
tests.AssertNotNil(t, token) tests.AssertNotNil(t, token)