Compare commits

...

1 Commits

Author SHA1 Message Date
5797a51993 initial steps for concurrent execution
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 23:03:59 +00:00
9 changed files with 250 additions and 32 deletions

View File

@ -54,6 +54,7 @@ class Challenge(PassiveSerializer):
flow_info = ContextualFlowInfo(required=False)
component = CharField(default="")
xid = CharField(required=False)
response_errors = DictField(
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False

View File

@ -143,10 +143,12 @@ class FlowPlan:
request: HttpRequest,
flow: Flow,
allowed_silent_types: list["StageView"] | None = None,
**get_params,
) -> HttpResponse:
"""Redirect to the flow executor for this flow plan"""
from authentik.flows.views.executor import (
SESSION_KEY_PLAN,
FlowContainer,
FlowExecutorView,
)
@ -157,6 +159,7 @@ class FlowPlan:
# No unskippable stages found, so we can directly return the response of the last stage
final_stage: type[StageView] = self.bindings[-1].stage.view
temp_exec = FlowExecutorView(flow=flow, request=request, plan=self)
temp_exec.container = FlowContainer(request)
temp_exec.current_stage = self.bindings[-1].stage
temp_exec.current_stage_view = final_stage
temp_exec.setup(request, flow.slug)
@ -174,6 +177,9 @@ class FlowPlan:
):
get_qs["inspector"] = "available"
for key, value in get_params:
get_qs[key] = value
return redirect_with_qs(
"authentik_core:if-flow",
get_qs,

View File

@ -191,6 +191,7 @@ class ChallengeStageView(StageView):
)
flow_info.is_valid()
challenge.initial_data["flow_info"] = flow_info.data
challenge.initial_data["xid"] = self.executor.container.exec_id
if isinstance(challenge, WithUserInfoChallenge):
# If there's a pending user, update the `username` field
# this field is only used by password managers.

View File

@ -28,7 +28,7 @@ window.authentik.flow = {
{% block body %}
<ak-message-container></ak-message-container>
<ak-flow-executor flowSlug="{{ flow.slug }}">
<ak-flow-executor flowSlug="{{ flow.slug }}" xid="{{ xid }}">
<ak-loading></ak-loading>
</ak-flow-executor>
{% endblock %}

View File

@ -1,6 +1,7 @@
"""authentik multi-stage authentication engine"""
from copy import deepcopy
from uuid import uuid4
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
@ -64,6 +65,7 @@ from authentik.policies.engine import PolicyEngine
LOGGER = get_logger()
# Argument used to redirect user after login
NEXT_ARG_NAME = "next"
SESSION_KEY_PLAN_CONTAINER = "authentik/flows/plan_container/%s"
SESSION_KEY_PLAN = "authentik/flows/plan"
SESSION_KEY_APPLICATION_PRE = "authentik/flows/application_pre"
SESSION_KEY_GET = "authentik/flows/get"
@ -71,6 +73,7 @@ SESSION_KEY_POST = "authentik/flows/post"
SESSION_KEY_HISTORY = "authentik/flows/history"
QS_KEY_TOKEN = "flow_token" # nosec
QS_QUERY = "query"
QS_EXEC_ID = "xid"
def challenge_types():
@ -97,6 +100,88 @@ class InvalidStageError(SentryIgnoredException):
"""Error raised when a challenge from a stage is not valid"""
class FlowContainer:
"""Allow for multiple concurrent flow executions in the same session"""
def __init__(self, request: HttpRequest, exec_id: str | None = None) -> None:
self.request = request
self.exec_id = exec_id
@staticmethod
def new(request: HttpRequest):
exec_id = str(uuid4())
request.session[SESSION_KEY_PLAN_CONTAINER % exec_id] = {}
return FlowContainer(request, exec_id)
def exists(self) -> bool:
"""Check if flow exists in container/session"""
return SESSION_KEY_PLAN in self.session
def save(self):
self.request.session.modified = True
@property
def session(self):
# Backwards compatibility: store session plan/etc directly in session
if not self.exec_id:
return self.request.session
self.request.session.setdefault(SESSION_KEY_PLAN_CONTAINER % self.exec_id, {})
return self.request.session.get(SESSION_KEY_PLAN_CONTAINER % self.exec_id, {})
@property
def plan(self) -> FlowPlan:
return self.session.get(SESSION_KEY_PLAN)
def to_redirect(
self,
request: HttpRequest,
flow: Flow,
allowed_silent_types: list[StageView] | None = None,
**get_params,
) -> HttpResponse:
get_params[QS_EXEC_ID] = self.exec_id
return self.plan.to_redirect(
request, flow, allowed_silent_types=allowed_silent_types, **get_params
)
@plan.setter
def plan(self, value: FlowPlan):
self.session[SESSION_KEY_PLAN] = value
self.request.session.modified = True
self.save()
@property
def application_pre(self):
return self.session.get(SESSION_KEY_APPLICATION_PRE)
@property
def get(self) -> QueryDict:
return self.session.get(SESSION_KEY_GET)
@get.setter
def get(self, value: QueryDict):
self.session[SESSION_KEY_GET] = value
self.save()
@property
def post(self) -> QueryDict:
return self.session.get(SESSION_KEY_POST)
@post.setter
def post(self, value: QueryDict):
self.session[SESSION_KEY_POST] = value
self.save()
@property
def history(self) -> list[FlowPlan]:
return self.session.get(SESSION_KEY_HISTORY)
@history.setter
def history(self, value: list[FlowPlan]):
self.session[SESSION_KEY_HISTORY] = value
self.save()
@method_decorator(xframe_options_sameorigin, name="dispatch")
class FlowExecutorView(APIView):
"""Flow executor, passing requests to Stage Views"""
@ -104,8 +189,9 @@ class FlowExecutorView(APIView):
permission_classes = [AllowAny]
flow: Flow = None
plan: FlowPlan | None = None
container: FlowContainer
current_binding: FlowStageBinding | None = None
current_stage: Stage
current_stage_view: View
@ -160,10 +246,12 @@ class FlowExecutorView(APIView):
if QS_KEY_TOKEN in get_params:
plan = self._check_flow_token(get_params[QS_KEY_TOKEN])
if plan:
self.request.session[SESSION_KEY_PLAN] = plan
container = FlowContainer.new(request)
container.plan = plan
# Early check if there's an active Plan for the current session
if SESSION_KEY_PLAN in self.request.session:
self.plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
self.container = FlowContainer(request, request.GET.get(QS_EXEC_ID))
if self.container.exists():
self.plan: FlowPlan = self.container.plan
if self.plan.flow_pk != self.flow.pk.hex:
self._logger.warning(
"f(exec): Found existing plan for other flow, deleting plan",
@ -176,13 +264,14 @@ class FlowExecutorView(APIView):
self._logger.debug("f(exec): Continuing existing plan")
# Initial flow request, check if we have an upstream query string passed in
request.session[SESSION_KEY_GET] = get_params
self.container.get = get_params
# Don't check session again as we've either already loaded the plan or we need to plan
if not self.plan:
request.session[SESSION_KEY_HISTORY] = []
self.container.history = []
self._logger.debug("f(exec): No active Plan found, initiating planner")
try:
self.plan = self._initiate_plan()
self.container.plan = self.plan
except FlowNonApplicableException as exc:
self._logger.warning("f(exec): Flow not applicable to current user", exc=exc)
return self.handle_invalid_flow(exc)
@ -254,12 +343,19 @@ class FlowExecutorView(APIView):
request=OpenApiTypes.NONE,
parameters=[
OpenApiParameter(
name="query",
name=QS_QUERY,
location=OpenApiParameter.QUERY,
required=True,
description="Querystring as received",
type=OpenApiTypes.STR,
)
),
OpenApiParameter(
name=QS_EXEC_ID,
location=OpenApiParameter.QUERY,
required=False,
description="Flow execution ID",
type=OpenApiTypes.STR,
),
],
operation_id="flows_executor_get",
)
@ -286,7 +382,7 @@ class FlowExecutorView(APIView):
span.set_data("authentik Stage", self.current_stage_view)
span.set_data("authentik Flow", self.flow.slug)
stage_response = self.current_stage_view.dispatch(request)
return to_stage_response(request, stage_response)
return to_stage_response(request, stage_response, self.container.exec_id)
except Exception as exc:
return self.handle_exception(exc)
@ -305,12 +401,19 @@ class FlowExecutorView(APIView):
),
parameters=[
OpenApiParameter(
name="query",
name=QS_QUERY,
location=OpenApiParameter.QUERY,
required=True,
description="Querystring as received",
type=OpenApiTypes.STR,
)
),
OpenApiParameter(
name=QS_EXEC_ID,
location=OpenApiParameter.QUERY,
required=True,
description="Flow execution ID",
type=OpenApiTypes.STR,
),
],
operation_id="flows_executor_solve",
)
@ -337,14 +440,15 @@ class FlowExecutorView(APIView):
span.set_data("authentik Stage", self.current_stage_view)
span.set_data("authentik Flow", self.flow.slug)
stage_response = self.current_stage_view.dispatch(request)
return to_stage_response(request, stage_response)
return to_stage_response(request, stage_response, self.container.exec_id)
except Exception as exc:
return self.handle_exception(exc)
def _initiate_plan(self) -> FlowPlan:
planner = FlowPlanner(self.flow)
plan = planner.plan(self.request)
self.request.session[SESSION_KEY_PLAN] = plan
container = FlowContainer.new(self.request)
container.plan = plan
try:
# Call the has_stages getter to check that
# there are no issues with the class we might've gotten
@ -368,7 +472,7 @@ class FlowExecutorView(APIView):
except FlowNonApplicableException as exc:
self._logger.warning("f(exec): Flow restart not applicable to current user", exc=exc)
return self.handle_invalid_flow(exc)
self.request.session[SESSION_KEY_PLAN] = plan
self.container.plan = plan
kwargs = self.kwargs
kwargs.update({"flow_slug": self.flow.slug})
return redirect_with_qs("authentik_api:flow-executor", self.request.GET, **kwargs)
@ -390,9 +494,13 @@ class FlowExecutorView(APIView):
)
self.cancel()
if next_param and not is_url_absolute(next_param):
return to_stage_response(self.request, redirect_with_qs(next_param))
return to_stage_response(
self.request, redirect_with_qs(next_param), self.container.exec_id
)
return to_stage_response(
self.request, self.stage_invalid(error_message=_("Invalid next URL"))
self.request,
self.stage_invalid(error_message=_("Invalid next URL")),
self.container.exec_id,
)
def stage_ok(self) -> HttpResponse:
@ -406,7 +514,7 @@ class FlowExecutorView(APIView):
self.current_stage_view.cleanup()
self.request.session.get(SESSION_KEY_HISTORY, []).append(deepcopy(self.plan))
self.plan.pop()
self.request.session[SESSION_KEY_PLAN] = self.plan
self.container.plan = self.plan
if self.plan.bindings:
self._logger.debug(
"f(exec): Continuing with next stage",
@ -449,6 +557,7 @@ class FlowExecutorView(APIView):
def cancel(self):
"""Cancel current flow execution"""
# TODO: Clean up container
keys_to_delete = [
SESSION_KEY_APPLICATION_PRE,
SESSION_KEY_PLAN,
@ -471,8 +580,8 @@ class CancelView(View):
def get(self, request: HttpRequest) -> HttpResponse:
"""View which canels the currently active plan"""
if SESSION_KEY_PLAN in request.session:
del request.session[SESSION_KEY_PLAN]
if FlowContainer(request, request.GET.get(QS_EXEC_ID)).exists():
del request.session[SESSION_KEY_PLAN_CONTAINER % request.GET.get(QS_EXEC_ID)]
LOGGER.debug("Canceled current plan")
return redirect("authentik_flows:default-invalidation")
@ -520,19 +629,12 @@ class ToDefaultFlow(View):
def dispatch(self, request: HttpRequest) -> HttpResponse:
flow = self.get_flow()
# If user already has a pending plan, clear it so we don't have to later.
if SESSION_KEY_PLAN in self.request.session:
plan: FlowPlan = self.request.session[SESSION_KEY_PLAN]
if plan.flow_pk != flow.pk.hex:
LOGGER.warning(
"f(def): Found existing plan for other flow, deleting plan",
flow_slug=flow.slug,
)
del self.request.session[SESSION_KEY_PLAN]
return redirect_with_qs("authentik_core:if-flow", request.GET, flow_slug=flow.slug)
get_qs = request.GET.copy()
get_qs[QS_EXEC_ID] = str(uuid4())
return redirect_with_qs("authentik_core:if-flow", get_qs, flow_slug=flow.slug)
def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpResponse:
def to_stage_response(request: HttpRequest, source: HttpResponse, xid: str) -> HttpResponse:
"""Convert normal HttpResponse into JSON Response"""
if (
isinstance(source, HttpResponseRedirect)
@ -551,6 +653,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
RedirectChallenge(
{
"to": str(redirect_url),
"xid": xid,
}
)
)
@ -559,6 +662,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
ShellChallenge(
{
"body": source.render().content.decode("utf-8"),
"xid": xid,
}
)
)
@ -568,6 +672,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
ShellChallenge(
{
"body": source.content.decode("utf-8"),
"xid": xid,
}
)
)
@ -599,4 +704,6 @@ class ConfigureFlowInitView(LoginRequiredMixin, View):
except FlowNonApplicableException:
LOGGER.warning("Flow not applicable to user")
raise Http404 from None
return plan.to_redirect(request, stage.configure_flow)
container = FlowContainer.new(request)
container.plan = plan
return container.to_redirect(request, stage.configure_flow)

View File

@ -7,6 +7,7 @@ from ua_parser.user_agent_parser import Parse
from authentik.core.views.interface import InterfaceView
from authentik.flows.models import Flow
from authentik.flows.views.executor import QS_EXEC_ID
class FlowInterfaceView(InterfaceView):
@ -15,6 +16,7 @@ class FlowInterfaceView(InterfaceView):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
kwargs["flow"] = get_object_or_404(Flow, slug=self.kwargs.get("flow_slug"))
kwargs["inspector"] = "inspector" in self.request.GET
kwargs["xid"] = self.request.GET.get(QS_EXEC_ID)
return super().get_context_data(**kwargs)
def compat_needs_sfe(self) -> bool:

2
go.mod
View File

@ -82,3 +82,5 @@ require (
google.golang.org/protobuf v1.36.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace goauthentik.io/api/v3 => ./gen-go-api

View File

@ -8917,6 +8917,11 @@ paths:
type: string
description: Querystring as received
required: true
- in: query
name: xid
schema:
type: string
description: Flow execution ID
tags:
- flows
security:
@ -8957,6 +8962,12 @@ paths:
type: string
description: Querystring as received
required: true
- in: query
name: xid
schema:
type: string
description: Flow execution ID
required: true
tags:
- flows
requestBody:
@ -39437,6 +39448,8 @@ components:
component:
type: string
default: ak-stage-access-denied
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -39452,6 +39465,7 @@ components:
required:
- pending_user
- pending_user_avatar
- xid
AlgEnum:
enum:
- rsa
@ -39551,6 +39565,8 @@ components:
component:
type: string
default: ak-source-oauth-apple
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -39570,6 +39586,7 @@ components:
- redirect_uri
- scope
- state
- xid
Application:
type: object
description: Application Serializer
@ -39878,6 +39895,8 @@ components:
component:
type: string
default: ak-stage-authenticator-duo
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -39900,6 +39919,7 @@ components:
- pending_user
- pending_user_avatar
- stage_uuid
- xid
AuthenticatorDuoChallengeResponseRequest:
type: object
description: Pseudo class for duo response
@ -40037,6 +40057,8 @@ components:
component:
type: string
default: ak-stage-authenticator-email
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40056,6 +40078,7 @@ components:
required:
- pending_user
- pending_user_avatar
- xid
AuthenticatorEmailChallengeResponseRequest:
type: object
description: Authenticator Email Challenge response, device is set by get_response_instance
@ -40293,6 +40316,8 @@ components:
component:
type: string
default: ak-stage-authenticator-sms
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40309,6 +40334,7 @@ components:
required:
- pending_user
- pending_user_avatar
- xid
AuthenticatorSMSChallengeResponseRequest:
type: object
description: SMS Challenge response, device is set by get_response_instance
@ -40456,6 +40482,8 @@ components:
component:
type: string
default: ak-stage-authenticator-static
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40474,6 +40502,7 @@ components:
- codes
- pending_user
- pending_user_avatar
- xid
AuthenticatorStaticChallengeResponseRequest:
type: object
description: Pseudo class for static response
@ -40577,6 +40606,8 @@ components:
component:
type: string
default: ak-stage-authenticator-totp
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40593,6 +40624,7 @@ components:
- config_url
- pending_user
- pending_user_avatar
- xid
AuthenticatorTOTPChallengeResponseRequest:
type: object
description: TOTP Challenge response, device is set by get_response_instance
@ -40804,6 +40836,8 @@ components:
component:
type: string
default: ak-stage-authenticator-validate
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40827,6 +40861,7 @@ components:
- device_challenges
- pending_user
- pending_user_avatar
- xid
AuthenticatorValidationChallengeResponseRequest:
type: object
description: Challenge used for Code-based and WebAuthn authenticators
@ -40857,6 +40892,8 @@ components:
component:
type: string
default: ak-stage-authenticator-webauthn
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -40874,6 +40911,7 @@ components:
- pending_user
- pending_user_avatar
- registration
- xid
AuthenticatorWebAuthnChallengeResponseRequest:
type: object
description: WebAuthn Challenge response
@ -41006,6 +41044,8 @@ components:
component:
type: string
default: ak-stage-autosubmit
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -41023,6 +41063,7 @@ components:
required:
- attrs
- url
- xid
BackendsEnum:
enum:
- authentik.core.auth.InbuiltBackend
@ -41269,6 +41310,8 @@ components:
component:
type: string
default: ak-stage-captcha
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -41291,6 +41334,7 @@ components:
- pending_user
- pending_user_avatar
- site_key
- xid
CaptchaChallengeResponseRequest:
type: object
description: Validate captcha token
@ -41674,6 +41718,8 @@ components:
component:
type: string
default: ak-stage-consent
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -41702,6 +41748,7 @@ components:
- pending_user_avatar
- permissions
- token
- xid
ConsentChallengeResponseRequest:
type: object
description: Consent challenge response, any valid response request is valid
@ -42475,6 +42522,8 @@ components:
component:
type: string
default: ak-stage-dummy
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -42485,6 +42534,7 @@ components:
type: string
required:
- name
- xid
DummyChallengeResponseRequest:
type: object
description: Dummy challenge response
@ -42677,12 +42727,16 @@ components:
component:
type: string
default: ak-stage-email
xid:
type: string
response_errors:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- xid
EmailChallengeResponseRequest:
type: object
description: |-
@ -43601,6 +43655,8 @@ components:
component:
type: string
default: ak-stage-flow-error
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -43615,6 +43671,7 @@ components:
type: string
required:
- request_id
- xid
FlowImportResult:
type: object
description: Logs of an attempted flow import
@ -43929,6 +43986,8 @@ components:
component:
type: string
default: xak-flow-frame
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -43945,6 +44004,7 @@ components:
required:
- loading_text
- url
- xid
FrameChallengeResponseRequest:
type: object
description: Base class for all challenge responses
@ -44747,6 +44807,8 @@ components:
component:
type: string
default: ak-stage-identification
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -44791,6 +44853,7 @@ components:
- primary_action
- show_source_labels
- user_fields
- xid
IdentificationChallengeResponseRequest:
type: object
description: Identification challenge
@ -47233,12 +47296,16 @@ components:
component:
type: string
default: ak-provider-oauth2-device-code
xid:
type: string
response_errors:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- xid
OAuthDeviceCodeChallengeResponseRequest:
type: object
description: Response that includes the user-entered device code
@ -47261,12 +47328,16 @@ components:
component:
type: string
default: ak-provider-oauth2-device-code-finish
xid:
type: string
response_errors:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- xid
OAuthDeviceCodeFinishChallengeResponseRequest:
type: object
description: Response that device has been authenticated and tab can be closed
@ -49411,6 +49482,8 @@ components:
component:
type: string
default: ak-stage-password
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -49429,6 +49502,7 @@ components:
required:
- pending_user
- pending_user_avatar
- xid
PasswordChallengeResponseRequest:
type: object
description: Password challenge response
@ -52990,6 +53064,8 @@ components:
component:
type: string
default: ak-source-plex
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -53003,6 +53079,7 @@ components:
required:
- client_id
- slug
- xid
PlexAuthenticationChallengeResponseRequest:
type: object
description: Pseudo class for plex response
@ -53515,6 +53592,8 @@ components:
component:
type: string
default: ak-stage-prompt
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -53527,6 +53606,7 @@ components:
$ref: '#/components/schemas/StagePrompt'
required:
- fields
- xid
PromptChallengeResponseRequest:
type: object
description: |-
@ -54711,6 +54791,8 @@ components:
component:
type: string
default: xak-flow-redirect
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -54721,6 +54803,7 @@ components:
type: string
required:
- to
- xid
RedirectChallengeResponseRequest:
type: object
description: Redirect challenge response
@ -56616,6 +56699,8 @@ components:
component:
type: string
default: ak-stage-session-end
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -56638,6 +56723,7 @@ components:
- brand_name
- pending_user
- pending_user_avatar
- xid
SessionUser:
type: object
description: |-
@ -56750,6 +56836,8 @@ components:
component:
type: string
default: xak-flow-shell
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -56760,6 +56848,7 @@ components:
type: string
required:
- body
- xid
SignatureAlgorithmEnum:
enum:
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
@ -58034,6 +58123,8 @@ components:
component:
type: string
default: ak-stage-user-login
xid:
type: string
response_errors:
type: object
additionalProperties:
@ -58047,6 +58138,7 @@ components:
required:
- pending_user
- pending_user_avatar
- xid
UserLoginChallengeResponseRequest:
type: object
description: User login challenge

View File

@ -85,6 +85,9 @@ export class FlowExecutor extends Interface implements StageHost {
ws: WebsocketClient;
@property()
xid?: string;
static get styles(): CSSResult[] {
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css`
:host {
@ -219,6 +222,7 @@ export class FlowExecutor extends Interface implements StageHost {
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorSolve({
flowSlug: this.flowSlug,
query: window.location.search.substring(1),
xid: this.xid || "",
flowChallengeResponseRequest: payload,
});
if (this.inspectorOpen) {
@ -252,6 +256,7 @@ export class FlowExecutor extends Interface implements StageHost {
const challenge = await new FlowsApi(DEFAULT_CONFIG).flowsExecutorGet({
flowSlug: this.flowSlug,
query: window.location.search.substring(1),
xid: this.xid,
});
if (this.inspectorOpen) {
window.dispatchEvent(
@ -262,6 +267,7 @@ export class FlowExecutor extends Interface implements StageHost {
);
}
this.challenge = challenge;
this.xid = challenge.xid ?? undefined;
if (this.challenge.flowInfo) {
this.flowInfo = this.challenge.flowInfo;
}
@ -286,6 +292,7 @@ export class FlowExecutor extends Interface implements StageHost {
component: "ak-stage-flow-error",
error: body,
requestId: "",
xid: "",
};
this.challenge = challenge as ChallengeTypes;
}