diff --git a/authentik/brands/tests.py b/authentik/brands/tests.py
index e320340565..41d84e82df 100644
--- a/authentik/brands/tests.py
+++ b/authentik/brands/tests.py
@@ -148,3 +148,14 @@ class TestBrands(APITestCase):
"default_locale": "",
},
)
+
+ def test_custom_css(self):
+ """Test custom_css"""
+ brand = create_test_brand()
+ brand.branding_custom_css = """* {
+ font-family: "Foo bar";
+ }"""
+ brand.save()
+ res = self.client.get(reverse("authentik_core:if-user"))
+ self.assertEqual(res.status_code, 200)
+ self.assertIn(brand.branding_custom_css, res.content.decode())
diff --git a/authentik/brands/utils.py b/authentik/brands/utils.py
index 8f8fdf5e9b..3d39410524 100644
--- a/authentik/brands/utils.py
+++ b/authentik/brands/utils.py
@@ -5,6 +5,8 @@ from typing import Any
from django.db.models import F, Q
from django.db.models import Value as V
from django.http.request import HttpRequest
+from django.utils.html import _json_script_escapes
+from django.utils.safestring import mark_safe
from authentik import get_full_version
from authentik.brands.models import Brand
@@ -32,8 +34,13 @@ def context_processor(request: HttpRequest) -> dict[str, Any]:
"""Context Processor that injects brand object into every template"""
brand = getattr(request, "brand", DEFAULT_BRAND)
tenant = getattr(request, "tenant", Tenant())
+ # similarly to `json_script` we escape everything HTML-related, however django
+ # only directly exposes this as a function that also wraps it in a
{% block head %}