package handler import ( "context" "net/http" "strings" "img_infinite_canvas/internal/domain/design" ) type bearerAuthenticator interface { UserIDFromBearer(ctx context.Context, authorization string) (string, bool) } func UserContextMiddleware(authenticator bearerAuthenticator) func(http.HandlerFunc) http.HandlerFunc { return func(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("X-User-Id") authenticated := false if authenticator != nil { if tokenUserID, ok := userIDFromRequestAuth(r.Context(), r, authenticator); ok { userID = tokenUserID authenticated = true } } if requiresAuthenticatedUser(r) && !authenticated { writeAuthRequired(w) return } ctx := design.ContextWithUserID(r.Context(), userID) next(w, r.WithContext(ctx)) } } } func userIDFromRequestAuth(ctx context.Context, r *http.Request, authenticator bearerAuthenticator) (string, bool) { if tokenUserID, ok := authenticator.UserIDFromBearer(ctx, r.Header.Get("Authorization")); ok { return tokenUserID, true } if token := strings.TrimSpace(r.Header.Get("token")); token != "" { if tokenUserID, ok := authenticator.UserIDFromBearer(ctx, "Bearer "+token); ok { return tokenUserID, true } } if cookie, err := r.Cookie("usertoken"); err == nil && strings.TrimSpace(cookie.Value) != "" { if tokenUserID, ok := authenticator.UserIDFromBearer(ctx, "Bearer "+cookie.Value); ok { return tokenUserID, true } } return "", false } func requiresAuthenticatedUser(r *http.Request) bool { path := r.URL.Path if !strings.HasPrefix(path, "/api/") { return false } if strings.HasPrefix(path, "/api/auth/") || path == "/api/auth/options" || path == "/api/health" || path == "/api/dashboard" { return false } return strings.HasPrefix(path, "/api/projects") || strings.HasPrefix(path, "/api/account") || strings.HasPrefix(path, "/api/assets") || strings.HasPrefix(path, "/api/generator/") } func writeAuthRequired(w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json; charset=utf-8") w.WriteHeader(http.StatusUnauthorized) _, _ = w.Write([]byte(`{"error":"login required","errorCode":"auth.login_required","code":401}`)) } func LegacyUserContextMiddleware(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { userID := r.Header.Get("X-User-Id") ctx := design.ContextWithUserID(r.Context(), userID) next(w, r.WithContext(ctx)) } }