From abdff1c877e7ff95a5f5af99b65f11dad40abf9a Mon Sep 17 00:00:00 2001 From: Dominic R Date: Thu, 30 Apr 2026 07:53:41 -0400 Subject: [PATCH] flows: preserve signed background URLs in CSS (#21868) * flows: preserve signed background URLs in CSS Flow background URLs can include signed S3 query parameters with & separators. These values are rendered inside diff --git a/authentik/flows/templates/if/flow-sfe.html b/authentik/flows/templates/if/flow-sfe.html index 39f528d416..311b3f139e 100644 --- a/authentik/flows/templates/if/flow-sfe.html +++ b/authentik/flows/templates/if/flow-sfe.html @@ -23,7 +23,7 @@ height: 100%; } body { - background-image: url("{{ flow_background_url }}"); + background-image: url("{{ flow_background_url|iriencode|safe }}"); background-repeat: no-repeat; background-size: cover; } diff --git a/authentik/flows/templates/if/flow.html b/authentik/flows/templates/if/flow.html index ea5d0f60cb..848e535040 100644 --- a/authentik/flows/templates/if/flow.html +++ b/authentik/flows/templates/if/flow.html @@ -39,7 +39,7 @@ {% endblock %} diff --git a/authentik/flows/tests/test_stage_views.py b/authentik/flows/tests/test_stage_views.py index a7379c7498..458d237803 100644 --- a/authentik/flows/tests/test_stage_views.py +++ b/authentik/flows/tests/test_stage_views.py @@ -1,12 +1,14 @@ """stage view tests""" from collections.abc import Callable +from unittest.mock import patch from django.test import RequestFactory, TestCase +from django.urls import reverse from authentik.core.tests.utils import RequestFactory as AuthentikRequestFactory from authentik.core.tests.utils import create_test_flow -from authentik.flows.models import FlowStageBinding +from authentik.flows.models import Flow, FlowStageBinding from authentik.flows.stage import StageView from authentik.flows.views.executor import FlowExecutorView from authentik.lib.utils.reflection import all_subclasses @@ -42,6 +44,46 @@ class TestViews(TestCase): "/static/dist/assets/images/flow_background.jpg", ) + def test_flow_interface_css_background_preserves_presigned_url_query(self): + """Test flow CSS keeps signed URL query separators intact.""" + flow = create_test_flow() + background_url = ( + "https://s3.ca-central-1.amazonaws.com/example/media/public/background.png" + "?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=credential" + "&X-Amz-Signature=signature" + ) + + with patch.object(Flow, "background_url", return_value=background_url): + response = self.client.get( + reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug}) + ) + + self.assertContains( + response, + f'--ak-global--background-image: url("{background_url}");', + html=False, + ) + + def test_flow_sfe_css_background_preserves_presigned_url_query(self): + """Test SFE flow CSS keeps signed URL query separators intact.""" + flow = create_test_flow() + background_url = ( + "https://s3.ca-central-1.amazonaws.com/example/media/public/background.png" + "?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=credential" + "&X-Amz-Signature=signature" + ) + + with patch.object(Flow, "background_url", return_value=background_url): + response = self.client.get( + reverse("authentik_core:if-flow", kwargs={"flow_slug": flow.slug}) + "?sfe" + ) + + self.assertContains( + response, + f'background-image: url("{background_url}");', + html=False, + ) + def view_tester_factory(view_class: type[StageView]) -> Callable: """Test a form"""