flows: fix redirect when un-authenticated user uses external authentication (#416)
* flows: add PLAN_CONTEXT_REDIRECT so final redirect can be set from within flow * sources/*: use PLAN_CONTEXT_REDIRECT * flows: fallback when flow plan is empty
This commit is contained in:
parent
98a58b74e3
commit
6e24856d45
|
@ -19,6 +19,7 @@ LOGGER = get_logger()
|
|||
|
||||
PLAN_CONTEXT_PENDING_USER = "pending_user"
|
||||
PLAN_CONTEXT_SSO = "is_sso"
|
||||
PLAN_CONTEXT_REDIRECT = "redirect"
|
||||
PLAN_CONTEXT_APPLICATION = "application"
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,12 @@ from authentik.audit.models import cleanse_dict
|
|||
from authentik.core.models import USER_ATTRIBUTE_DEBUG
|
||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||
from authentik.flows.models import ConfigurableStage, Flow, FlowDesignation, Stage
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan, FlowPlanner
|
||||
from authentik.flows.planner import (
|
||||
PLAN_CONTEXT_PENDING_USER,
|
||||
PLAN_CONTEXT_REDIRECT,
|
||||
FlowPlan,
|
||||
FlowPlanner,
|
||||
)
|
||||
from authentik.lib.utils.reflection import class_to_path
|
||||
from authentik.lib.utils.urls import is_url_absolute, redirect_with_qs
|
||||
from authentik.policies.http import AccessDeniedResponse
|
||||
|
@ -145,9 +150,13 @@ class FlowExecutorView(View):
|
|||
"""User Successfully passed all stages"""
|
||||
# Since this is wrapped by the ExecutorShell, the next argument is saved in the session
|
||||
# extract the next param before cancel as that cleans it
|
||||
next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||
NEXT_ARG_NAME, "authentik_core:shell"
|
||||
)
|
||||
next_param = None
|
||||
if self.plan:
|
||||
next_param = self.plan.context.get(PLAN_CONTEXT_REDIRECT)
|
||||
if not next_param:
|
||||
next_param = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||
NEXT_ARG_NAME, "authentik_core:shell"
|
||||
)
|
||||
self.cancel()
|
||||
return to_stage_response(self.request, redirect_with_qs(next_param))
|
||||
|
||||
|
|
|
@ -15,10 +15,11 @@ from authentik.core.models import User
|
|||
from authentik.flows.models import Flow, in_memory_stage
|
||||
from authentik.flows.planner import (
|
||||
PLAN_CONTEXT_PENDING_USER,
|
||||
PLAN_CONTEXT_REDIRECT,
|
||||
PLAN_CONTEXT_SSO,
|
||||
FlowPlanner,
|
||||
)
|
||||
from authentik.flows.views import SESSION_KEY_PLAN
|
||||
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
||||
from authentik.lib.utils.urls import redirect_with_qs
|
||||
from authentik.policies.utils import delete_none_keys
|
||||
from authentik.sources.oauth.auth import AuthorizedServiceBackend
|
||||
|
@ -135,11 +136,17 @@ class OAuthCallback(OAuthClientMixin, View):
|
|||
|
||||
def handle_login_flow(self, flow: Flow, **kwargs) -> HttpResponse:
|
||||
"""Prepare Authentication Plan, redirect user FlowExecutor"""
|
||||
# Ensure redirect is carried through when user was trying to
|
||||
# authorize application
|
||||
final_redirect = self.request.session.get(SESSION_KEY_GET, {}).get(
|
||||
NEXT_ARG_NAME, "authentik_core:shell"
|
||||
)
|
||||
kwargs.update(
|
||||
{
|
||||
# Since we authenticate the user by their token, they have no backend set
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND: "django.contrib.auth.backends.ModelBackend",
|
||||
PLAN_CONTEXT_SSO: True,
|
||||
PLAN_CONTEXT_REDIRECT: final_redirect,
|
||||
}
|
||||
)
|
||||
# We run the Flow planner here so we can pass the Pending user in the context
|
||||
|
|
|
@ -13,10 +13,11 @@ from authentik.core.models import User
|
|||
from authentik.flows.models import Flow
|
||||
from authentik.flows.planner import (
|
||||
PLAN_CONTEXT_PENDING_USER,
|
||||
PLAN_CONTEXT_REDIRECT,
|
||||
PLAN_CONTEXT_SSO,
|
||||
FlowPlanner,
|
||||
)
|
||||
from authentik.flows.views import SESSION_KEY_PLAN
|
||||
from authentik.flows.views import NEXT_ARG_NAME, SESSION_KEY_GET, SESSION_KEY_PLAN
|
||||
from authentik.lib.utils.urls import redirect_with_qs
|
||||
from authentik.policies.utils import delete_none_keys
|
||||
from authentik.sources.saml.exceptions import (
|
||||
|
@ -54,11 +55,14 @@ class ResponseProcessor:
|
|||
_root: Any
|
||||
_root_xml: str
|
||||
|
||||
_http_request: HttpRequest
|
||||
|
||||
def __init__(self, source: SAMLSource):
|
||||
self._source = source
|
||||
|
||||
def parse(self, request: HttpRequest):
|
||||
"""Check if `request` contains SAML Response data, parse and validate it."""
|
||||
self._http_request = request
|
||||
# First off, check if we have any SAML Data at all.
|
||||
raw_response = request.POST.get("SAMLResponse", None)
|
||||
if not raw_response:
|
||||
|
@ -187,6 +191,11 @@ class ResponseProcessor:
|
|||
|
||||
name_id_filter = self._get_name_id_filter()
|
||||
matching_users = User.objects.filter(**name_id_filter)
|
||||
# Ensure redirect is carried through when user was trying to
|
||||
# authorize application
|
||||
final_redirect = self._http_request.session.get(SESSION_KEY_GET, {}).get(
|
||||
NEXT_ARG_NAME, "authentik_core:shell"
|
||||
)
|
||||
if matching_users.exists():
|
||||
# User exists already, switch to authentication flow
|
||||
return self._flow_response(
|
||||
|
@ -195,6 +204,7 @@ class ResponseProcessor:
|
|||
**{
|
||||
PLAN_CONTEXT_PENDING_USER: matching_users.first(),
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND: DEFAULT_BACKEND,
|
||||
PLAN_CONTEXT_REDIRECT: final_redirect,
|
||||
},
|
||||
)
|
||||
return self._flow_response(
|
||||
|
|
Reference in a new issue