diff --git a/authentik/core/expression/evaluator.py b/authentik/core/expression/evaluator.py
index 71557144a..85586c840 100644
--- a/authentik/core/expression/evaluator.py
+++ b/authentik/core/expression/evaluator.py
@@ -8,6 +8,7 @@ from django.http import HttpRequest
from authentik.core.models import User
from authentik.events.models import Event, EventAction
from authentik.lib.expression.evaluator import BaseEvaluator
+from authentik.lib.utils.errors import exception_to_string
from authentik.policies.types import PolicyRequest
@@ -38,7 +39,7 @@ class PropertyMappingEvaluator(BaseEvaluator):
def handle_error(self, exc: Exception, expression_source: str):
"""Exception Handler"""
- error_string = "\n".join(format_tb(exc.__traceback__) + [str(exc)])
+ error_string = exception_to_string(exc)
event = Event.new(
EventAction.PROPERTY_MAPPING_EXCEPTION,
expression=expression_source,
diff --git a/authentik/flows/challenge.py b/authentik/flows/challenge.py
index 15cea8b92..78645bb75 100644
--- a/authentik/flows/challenge.py
+++ b/authentik/flows/challenge.py
@@ -1,9 +1,9 @@
"""Challenge helpers"""
from dataclasses import asdict, is_dataclass
from enum import Enum
-from traceback import format_tb
from typing import TYPE_CHECKING, Optional, TypedDict
from uuid import UUID
+from rest_framework.request import Request
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
@@ -11,6 +11,7 @@ from django.http import JsonResponse
from rest_framework.fields import CharField, ChoiceField, DictField
from authentik.core.api.utils import PassiveSerializer
+from authentik.lib.utils.errors import exception_to_string
if TYPE_CHECKING:
from authentik.flows.stage import StageView
@@ -90,32 +91,31 @@ class WithUserInfoChallenge(Challenge):
pending_user_avatar = CharField()
-class FlowErrorChallenge(WithUserInfoChallenge):
+class FlowErrorChallenge(Challenge):
"""Challenge class when an unhandled error occurs during a stage. Normal users
are shown an error message, superusers are shown a full stacktrace."""
- component = CharField(default="xak-flow-error")
+ type = CharField(default=ChallengeTypes.NATIVE.value)
+ component = CharField(default="ak-stage-flow-error")
request_id = CharField()
error = CharField(required=False)
traceback = CharField(required=False)
- def __init__(self, *args, **kwargs):
- request = kwargs.pop("request", None)
- error = kwargs.pop("error", None)
- super().__init__(*args, **kwargs)
+ def __init__(self, request: Optional[Request] = None, error: Optional[Exception] = None):
+ super().__init__(data={})
if not request or not error:
return
- self.request_id = request.request_id
+ self.initial_data["request_id"] = request.request_id
from authentik.core.models import USER_ATTRIBUTE_DEBUG
if request.user and request.user.is_authenticated:
if request.user.is_superuser or request.user.group_attributes(request).get(
USER_ATTRIBUTE_DEBUG, False
):
- self.error = error
- self.traceback = "".join(format_tb(self.error.__traceback__))
+ self.initial_data["error"] = str(error)
+ self.initial_data["traceback"] = exception_to_string(error)
class AccessDeniedChallenge(WithUserInfoChallenge):
diff --git a/authentik/flows/views/executor.py b/authentik/flows/views/executor.py
index cce3fa6d0..4fc5b5e52 100644
--- a/authentik/flows/views/executor.py
+++ b/authentik/flows/views/executor.py
@@ -255,7 +255,7 @@ class FlowExecutorView(APIView):
message=exception_to_string(exc),
).from_http(self.request)
challenge = FlowErrorChallenge(self.request, exc)
- challenge.is_valid()
+ challenge.is_valid(raise_exception=True)
return to_stage_response(self.request, HttpChallengeResponse(challenge))
@extend_schema(
diff --git a/schema.yml b/schema.yml
index ac68062cf..b21de6262 100644
--- a/schema.yml
+++ b/schema.yml
@@ -26611,7 +26611,7 @@ components:
ak-stage-consent: '#/components/schemas/ConsentChallenge'
ak-stage-dummy: '#/components/schemas/DummyChallenge'
ak-stage-email: '#/components/schemas/EmailChallenge'
- xak-flow-error: '#/components/schemas/FlowErrorChallenge'
+ ak-stage-flow-error: '#/components/schemas/FlowErrorChallenge'
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
ak-provider-oauth2-device-code: '#/components/schemas/OAuthDeviceCodeChallenge'
ak-provider-oauth2-device-code-finish: '#/components/schemas/OAuthDeviceCodeFinishChallenge'
@@ -27876,22 +27876,19 @@ components:
are shown an error message, superusers are shown a full stacktrace.
properties:
type:
- $ref: '#/components/schemas/ChallengeChoices'
+ type: string
+ default: native
flow_info:
$ref: '#/components/schemas/ContextualFlowInfo'
component:
type: string
- default: xak-flow-error
+ default: ak-stage-flow-error
response_errors:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
- pending_user:
- type: string
- pending_user_avatar:
- type: string
request_id:
type: string
error:
@@ -27899,10 +27896,7 @@ components:
traceback:
type: string
required:
- - pending_user
- - pending_user_avatar
- request_id
- - type
FlowImportResult:
type: object
description: Logs of an attempted flow import
diff --git a/web/src/flow/FlowExecutor.ts b/web/src/flow/FlowExecutor.ts
index 8e648479c..7e8f3dd8a 100644
--- a/web/src/flow/FlowExecutor.ts
+++ b/web/src/flow/FlowExecutor.ts
@@ -10,6 +10,7 @@ import { first } from "@goauthentik/common/utils";
import { WebsocketClient } from "@goauthentik/common/ws";
import { AKElement } from "@goauthentik/elements/Base";
import "@goauthentik/elements/LoadingOverlay";
+import "@goauthentik/flow/stages/FlowErrorStage";
import "@goauthentik/flow/stages/RedirectStage";
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
// Import webauthn-related stages to prevent issues on safari
@@ -45,6 +46,7 @@ import {
ChallengeTypes,
CurrentTenant,
FlowChallengeResponseRequest,
+ FlowErrorChallenge,
FlowsApi,
LayoutEnum,
RedirectChallenge,
@@ -107,10 +109,6 @@ export class FlowExecutor extends AKElement implements StageHost {
:host {
position: relative;
}
- .ak-exception {
- font-family: monospace;
- overflow-x: scroll;
- }
.pf-c-drawer__content {
background-color: transparent;
}
@@ -254,27 +252,13 @@ export class FlowExecutor extends AKElement implements StageHost {
} else if (error instanceof Error) {
body = error.message;
}
- this.challenge = {
- type: ChallengeChoices.Shell,
- body: `
- ${t`Whoops!`}
-
-
${body}-