switch saml to flow context

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens Langhammer
2025-06-13 16:05:52 +02:00
parent 8fb7b5160d
commit 430678326d
3 changed files with 22 additions and 14 deletions

View File

@ -35,8 +35,8 @@ REQUEST_KEY_SAML_SIG_ALG = "SigAlg"
REQUEST_KEY_SAML_RESPONSE = "SAMLResponse" REQUEST_KEY_SAML_RESPONSE = "SAMLResponse"
REQUEST_KEY_RELAY_STATE = "RelayState" REQUEST_KEY_RELAY_STATE = "RelayState"
SESSION_KEY_AUTH_N_REQUEST = "authentik/providers/saml/authn_request" PLAN_CONTEXT_SAML_AUTH_N_REQUEST = "authentik/providers/saml/authn_request"
SESSION_KEY_LOGOUT_REQUEST = "authentik/providers/saml/logout_request" PLAN_CONTEXT_SAML_LOGOUT_REQUEST = "authentik/providers/saml/logout_request"
# This View doesn't have a URL on purpose, as its called by the FlowExecutor # This View doesn't have a URL on purpose, as its called by the FlowExecutor
@ -50,10 +50,11 @@ class SAMLFlowFinalView(ChallengeStageView):
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION] application: Application = self.executor.plan.context[PLAN_CONTEXT_APPLICATION]
provider: SAMLProvider = get_object_or_404(SAMLProvider, pk=application.provider_id) provider: SAMLProvider = get_object_or_404(SAMLProvider, pk=application.provider_id)
if SESSION_KEY_AUTH_N_REQUEST not in self.request.session: if PLAN_CONTEXT_SAML_AUTH_N_REQUEST not in self.executor.plan.context:
self.logger.warning("No AuthNRequest in context")
return self.executor.stage_invalid() return self.executor.stage_invalid()
auth_n_request: AuthNRequest = self.request.session.pop(SESSION_KEY_AUTH_N_REQUEST) auth_n_request: AuthNRequest = self.executor.plan.context[PLAN_CONTEXT_SAML_AUTH_N_REQUEST]
try: try:
response = AssertionProcessor(provider, request, auth_n_request).build_response() response = AssertionProcessor(provider, request, auth_n_request).build_response()
except SAMLException as exc: except SAMLException as exc:
@ -106,6 +107,3 @@ class SAMLFlowFinalView(ChallengeStageView):
def challenge_valid(self, response: ChallengeResponse) -> HttpResponse: def challenge_valid(self, response: ChallengeResponse) -> HttpResponse:
# We'll never get here since the challenge redirects to the SP # We'll never get here since the challenge redirects to the SP
return HttpResponseBadRequest() return HttpResponseBadRequest()
def cleanup(self):
self.request.session.pop(SESSION_KEY_AUTH_N_REQUEST, None)

View File

@ -19,9 +19,9 @@ from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLProvider from authentik.providers.saml.models import SAMLProvider
from authentik.providers.saml.processors.logout_request_parser import LogoutRequestParser from authentik.providers.saml.processors.logout_request_parser import LogoutRequestParser
from authentik.providers.saml.views.flows import ( from authentik.providers.saml.views.flows import (
PLAN_CONTEXT_SAML_LOGOUT_REQUEST,
REQUEST_KEY_RELAY_STATE, REQUEST_KEY_RELAY_STATE,
REQUEST_KEY_SAML_REQUEST, REQUEST_KEY_SAML_REQUEST,
SESSION_KEY_LOGOUT_REQUEST,
) )
LOGGER = get_logger() LOGGER = get_logger()
@ -33,6 +33,10 @@ class SAMLSLOView(PolicyAccessView):
flow: Flow flow: Flow
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.plan_context = {}
def resolve_provider_application(self): def resolve_provider_application(self):
self.application = get_object_or_404(Application, slug=self.kwargs["application_slug"]) self.application = get_object_or_404(Application, slug=self.kwargs["application_slug"])
self.provider: SAMLProvider = get_object_or_404( self.provider: SAMLProvider = get_object_or_404(
@ -59,6 +63,7 @@ class SAMLSLOView(PolicyAccessView):
request, request,
{ {
PLAN_CONTEXT_APPLICATION: self.application, PLAN_CONTEXT_APPLICATION: self.application,
**self.plan_context,
}, },
) )
plan.append_stage(in_memory_stage(SessionEndStage)) plan.append_stage(in_memory_stage(SessionEndStage))
@ -83,7 +88,7 @@ class SAMLSLOBindingRedirectView(SAMLSLOView):
self.request.GET[REQUEST_KEY_SAML_REQUEST], self.request.GET[REQUEST_KEY_SAML_REQUEST],
relay_state=self.request.GET.get(REQUEST_KEY_RELAY_STATE, None), relay_state=self.request.GET.get(REQUEST_KEY_RELAY_STATE, None),
) )
self.request.session[SESSION_KEY_LOGOUT_REQUEST] = logout_request self.plan_context[PLAN_CONTEXT_SAML_LOGOUT_REQUEST] = logout_request
except CannotHandleAssertion as exc: except CannotHandleAssertion as exc:
Event.new( Event.new(
EventAction.CONFIGURATION_ERROR, EventAction.CONFIGURATION_ERROR,
@ -111,7 +116,7 @@ class SAMLSLOBindingPOSTView(SAMLSLOView):
payload[REQUEST_KEY_SAML_REQUEST], payload[REQUEST_KEY_SAML_REQUEST],
relay_state=payload.get(REQUEST_KEY_RELAY_STATE, None), relay_state=payload.get(REQUEST_KEY_RELAY_STATE, None),
) )
self.request.session[SESSION_KEY_LOGOUT_REQUEST] = logout_request self.plan_context[PLAN_CONTEXT_SAML_LOGOUT_REQUEST] = logout_request
except CannotHandleAssertion as exc: except CannotHandleAssertion as exc:
LOGGER.info(str(exc)) LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc)) return bad_request_message(self.request, str(exc))

View File

@ -20,11 +20,11 @@ from authentik.providers.saml.exceptions import CannotHandleAssertion
from authentik.providers.saml.models import SAMLBindings, SAMLProvider from authentik.providers.saml.models import SAMLBindings, SAMLProvider
from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser from authentik.providers.saml.processors.authn_request_parser import AuthNRequestParser
from authentik.providers.saml.views.flows import ( from authentik.providers.saml.views.flows import (
PLAN_CONTEXT_SAML_AUTH_N_REQUEST,
REQUEST_KEY_RELAY_STATE, REQUEST_KEY_RELAY_STATE,
REQUEST_KEY_SAML_REQUEST, REQUEST_KEY_SAML_REQUEST,
REQUEST_KEY_SAML_SIG_ALG, REQUEST_KEY_SAML_SIG_ALG,
REQUEST_KEY_SAML_SIGNATURE, REQUEST_KEY_SAML_SIGNATURE,
SESSION_KEY_AUTH_N_REQUEST,
SAMLFlowFinalView, SAMLFlowFinalView,
) )
from authentik.stages.consent.stage import ( from authentik.stages.consent.stage import (
@ -39,6 +39,10 @@ class SAMLSSOView(BufferedPolicyAccessView):
"""SAML SSO Base View, which plans a flow and injects our final stage. """SAML SSO Base View, which plans a flow and injects our final stage.
Calls get/post handler.""" Calls get/post handler."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.plan_context = {}
def resolve_provider_application(self): def resolve_provider_application(self):
self.application = get_object_or_404(Application, slug=self.kwargs["application_slug"]) self.application = get_object_or_404(Application, slug=self.kwargs["application_slug"])
self.provider: SAMLProvider = get_object_or_404( self.provider: SAMLProvider = get_object_or_404(
@ -68,6 +72,7 @@ class SAMLSSOView(BufferedPolicyAccessView):
PLAN_CONTEXT_CONSENT_HEADER: _("You're about to sign into %(application)s.") PLAN_CONTEXT_CONSENT_HEADER: _("You're about to sign into %(application)s.")
% {"application": self.application.name}, % {"application": self.application.name},
PLAN_CONTEXT_CONSENT_PERMISSIONS: [], PLAN_CONTEXT_CONSENT_PERMISSIONS: [],
**self.plan_context,
}, },
) )
except FlowNonApplicableException: except FlowNonApplicableException:
@ -103,7 +108,7 @@ class SAMLSSOBindingRedirectView(SAMLSSOView):
self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE), self.request.GET.get(REQUEST_KEY_SAML_SIGNATURE),
self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG), self.request.GET.get(REQUEST_KEY_SAML_SIG_ALG),
) )
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request self.plan_context[PLAN_CONTEXT_SAML_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc: except CannotHandleAssertion as exc:
Event.new( Event.new(
EventAction.CONFIGURATION_ERROR, EventAction.CONFIGURATION_ERROR,
@ -137,7 +142,7 @@ class SAMLSSOBindingPOSTView(SAMLSSOView):
payload[REQUEST_KEY_SAML_REQUEST], payload[REQUEST_KEY_SAML_REQUEST],
payload.get(REQUEST_KEY_RELAY_STATE), payload.get(REQUEST_KEY_RELAY_STATE),
) )
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request self.plan_context[PLAN_CONTEXT_SAML_AUTH_N_REQUEST] = auth_n_request
except CannotHandleAssertion as exc: except CannotHandleAssertion as exc:
LOGGER.info(str(exc)) LOGGER.info(str(exc))
return bad_request_message(self.request, str(exc)) return bad_request_message(self.request, str(exc))
@ -151,4 +156,4 @@ class SAMLSSOBindingInitView(SAMLSSOView):
"""Create SAML Response from scratch""" """Create SAML Response from scratch"""
LOGGER.debug("No SAML Request, using IdP-initiated flow.") LOGGER.debug("No SAML Request, using IdP-initiated flow.")
auth_n_request = AuthNRequestParser(self.provider).idp_initiated() auth_n_request = AuthNRequestParser(self.provider).idp_initiated()
self.request.session[SESSION_KEY_AUTH_N_REQUEST] = auth_n_request self.plan_context[PLAN_CONTEXT_SAML_AUTH_N_REQUEST] = auth_n_request