package middleware import ( "github.com/gin-gonic/gin" "github.com/pocket-id/pocket-id/backend/internal/apikey" "github.com/pocket-id/pocket-id/backend/internal/apperror" "github.com/pocket-id/pocket-id/backend/internal/service" ) // AuthMiddleware is a wrapper middleware that delegates to either API key or JWT authentication type AuthMiddleware struct { apiKeyMiddleware *ApiKeyAuthMiddleware jwtMiddleware *JwtAuthMiddleware options AuthOptions } type AuthOptions struct { AdminRequired bool SuccessOptional bool AllowApiKeyAuth bool } func NewAuthMiddleware( apiKeyModule *apikey.Module, userService *service.UserService, jwtService *service.JwtService, ) *AuthMiddleware { return &AuthMiddleware{ apiKeyMiddleware: NewApiKeyAuthMiddleware(apiKeyModule, jwtService), jwtMiddleware: NewJwtAuthMiddleware(jwtService, userService), options: AuthOptions{ AdminRequired: true, SuccessOptional: false, AllowApiKeyAuth: true, }, } } // WithAdminNotRequired allows the middleware to continue with the request even if the user is not an admin func (m *AuthMiddleware) WithAdminNotRequired() *AuthMiddleware { // Create a new instance to avoid modifying the original clone := &AuthMiddleware{ apiKeyMiddleware: m.apiKeyMiddleware, jwtMiddleware: m.jwtMiddleware, options: m.options, } clone.options.AdminRequired = false return clone } // WithSuccessOptional allows the middleware to continue with the request even if authentication fails func (m *AuthMiddleware) WithSuccessOptional() *AuthMiddleware { // Create a new instance to avoid modifying the original clone := &AuthMiddleware{ apiKeyMiddleware: m.apiKeyMiddleware, jwtMiddleware: m.jwtMiddleware, options: m.options, } clone.options.SuccessOptional = true return clone } // WithApiKeyAuthDisabled disables API key authentication fallback and requires JWT auth. func (m *AuthMiddleware) WithApiKeyAuthDisabled() *AuthMiddleware { clone := &AuthMiddleware{ apiKeyMiddleware: m.apiKeyMiddleware, jwtMiddleware: m.jwtMiddleware, options: m.options, } clone.options.AllowApiKeyAuth = false return clone } func (m *AuthMiddleware) Add() gin.HandlerFunc { return func(c *gin.Context) { userID, isAdmin, authenticationMethod, authenticationTime, err := m.jwtMiddleware.Verify(c, m.options.AdminRequired) if err == nil { c.Set("userID", userID) c.Set("userIsAdmin", isAdmin) c.Set("authenticationMethod", authenticationMethod) c.Set("authenticationTime", authenticationTime) if c.IsAborted() { return } c.Next() return } // If JWT auth failed for a reason other than missing credentials, abort the request if !apperror.IsCode(err, apperror.CodeNotSignedIn) { c.Abort() _ = c.Error(err) return } if !m.options.AllowApiKeyAuth { if m.options.SuccessOptional { c.Next() return } c.Abort() if c.GetHeader("X-API-Key") != "" { _ = c.Error(apperror.APIKeyAuthNotAllowed()) return } _ = c.Error(err) return } // JWT auth failed, try API key auth userID, isAdmin, err = m.apiKeyMiddleware.Verify(c, m.options.AdminRequired) if err == nil { c.Set("userID", userID) c.Set("userIsAdmin", isAdmin) if c.IsAborted() { return } c.Next() return } if m.options.SuccessOptional { c.Next() return } // Both JWT and API key auth failed c.Abort() _ = c.Error(err) } }