Files
Bookra/apps/backend/internal/auth/middleware.go
T
Tomas Dvorak 48c3e15a38 cleanup
2026-05-05 09:48:07 +02:00

90 lines
2.2 KiB
Go

package auth
import (
"net/http"
"strings"
"bookra/apps/backend/internal/db"
"bookra/apps/backend/internal/domain"
"github.com/gin-gonic/gin"
)
const principalContextKey = "principal"
// DemoPrincipal is the auto-authenticated user in demo mode
var DemoPrincipal = domain.Principal{
Subject: "demo-owner",
Email: "demo@bookra.dev",
Name: "Demo User",
Role: "owner",
}
func RequireAuth(verifier *Verifier, repo db.Repository, demoMode bool) gin.HandlerFunc {
return func(c *gin.Context) {
// In demo mode, auto-authenticate as the demo user
if demoMode {
c.Set(principalContextKey, DemoPrincipal)
c.Next()
return
}
if verifier == nil || !verifier.Enabled() {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "auth_not_configured"})
return
}
header := c.GetHeader("Authorization")
tokenString, ok := strings.CutPrefix(header, "Bearer ")
if !ok || strings.TrimSpace(tokenString) == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "missing_bearer_token"})
return
}
claims, err := verifier.Verify(strings.TrimSpace(tokenString))
if err != nil {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid_token"})
return
}
subject, _ := claims["sub"].(string)
email, _ := claims["email"].(string)
name, _ := claims["name"].(string)
if name == "" {
name, _ = claims["display_name"].(string)
}
role, _ := claims["role"].(string)
if role == "" {
role = "authenticated"
}
if strings.TrimSpace(subject) == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid_token_subject"})
return
}
if repo != nil {
if err := repo.EnsureUserIdentity(c.Request.Context(), subject, email, name); err != nil {
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"error": "identity_sync_failed"})
return
}
}
c.Set(principalContextKey, domain.Principal{
Subject: subject,
Email: email,
Name: name,
Role: role,
})
c.Next()
}
}
func PrincipalFromContext(c *gin.Context) domain.Principal {
value, ok := c.Get(principalContextKey)
if !ok {
return domain.Principal{}
}
principal, _ := value.(domain.Principal)
return principal
}