From d7e399dbf9a1ea11d75d4a54dde7666d95b500a1 Mon Sep 17 00:00:00 2001
From: Roney Dsilva <116948023+roney492@users.noreply.github.com>
Date: Mon, 25 Mar 2024 17:24:40 +0530
Subject: [PATCH] web/flow: general ux improvements (#8558)
* message fixes
* format
Signed-off-by: Jens Langhammer
* remove inline css, reword
Signed-off-by: Jens Langhammer
* don't rely on flow naming to show message
Signed-off-by: Jens Langhammer
* fix tests
Signed-off-by: Jens Langhammer
---------
Signed-off-by: Jens Langhammer
Co-authored-by: roney
Co-authored-by: Jens Langhammer
---
authentik/flows/tests/test_inspector.py | 1 +
.../authenticator_validate/challenge.py | 4 +-
authentik/stages/identification/stage.py | 6 +-
schema.yml | 27 +++++++--
.../providers/rac/ConnectionTokenList.ts | 4 +-
.../AuthenticatorTOTPStage.stories.ts | 58 +++++++++++++++++++
.../AuthenticatorTOTPStage.ts | 5 ++
.../identification/IdentificationStage.ts | 14 ++++-
8 files changed, 106 insertions(+), 13 deletions(-)
create mode 100644 web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.stories.ts
diff --git a/authentik/flows/tests/test_inspector.py b/authentik/flows/tests/test_inspector.py
index 49ba593566..a12054fd37 100644
--- a/authentik/flows/tests/test_inspector.py
+++ b/authentik/flows/tests/test_inspector.py
@@ -53,6 +53,7 @@ class TestFlowInspector(APITestCase):
"title": flow.title,
"layout": "stacked",
},
+ "flow_designation": "authentication",
"type": ChallengeTypes.NATIVE.value,
"password_fields": False,
"primary_action": "Log in",
diff --git a/authentik/stages/authenticator_validate/challenge.py b/authentik/stages/authenticator_validate/challenge.py
index a762e44c17..ce0dea1a16 100644
--- a/authentik/stages/authenticator_validate/challenge.py
+++ b/authentik/stages/authenticator_validate/challenge.py
@@ -120,7 +120,9 @@ def validate_challenge_code(code: str, stage_view: StageView, user: User) -> Dev
stage=stage_view.executor.current_stage,
device_class=DeviceClasses.TOTP.value,
)
- raise ValidationError(_("Invalid Token"))
+ raise ValidationError(
+ _("Invalid Token. Please ensure the time on your device is accurate and try again.")
+ )
return device
diff --git a/authentik/stages/identification/stage.py b/authentik/stages/identification/stage.py
index 3799991d9f..8be41825d5 100644
--- a/authentik/stages/identification/stage.py
+++ b/authentik/stages/identification/stage.py
@@ -10,7 +10,7 @@ from django.db.models import Q
from django.http import HttpResponse
from django.utils.translation import gettext as _
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
-from rest_framework.fields import BooleanField, CharField, DictField, ListField
+from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField, ListField
from rest_framework.serializers import ValidationError
from sentry_sdk.hub import Hub
@@ -66,6 +66,7 @@ class IdentificationChallenge(Challenge):
user_fields = ListField(child=CharField(), allow_empty=True, allow_null=True)
password_fields = BooleanField()
application_pre = CharField(required=False)
+ flow_designation = ChoiceField(FlowDesignation.choices)
enroll_url = CharField(required=False)
recovery_url = CharField(required=False)
@@ -194,11 +195,12 @@ class IdentificationStageView(ChallengeStageView):
challenge = IdentificationChallenge(
data={
"type": ChallengeTypes.NATIVE.value,
- "primary_action": self.get_primary_action(),
"component": "ak-stage-identification",
+ "primary_action": self.get_primary_action(),
"user_fields": current_stage.user_fields,
"password_fields": bool(current_stage.password_stage),
"show_source_labels": current_stage.show_source_labels,
+ "flow_designation": self.executor.flow.designation,
}
)
# If the user has been redirected to us whilst trying to access an
diff --git a/schema.yml b/schema.yml
index 6ac8eb9eb1..fd10552093 100644
--- a/schema.yml
+++ b/schema.yml
@@ -31782,8 +31782,7 @@ components:
pk:
type: string
format: uuid
- readOnly: true
- title: Pbm uuid
+ title: Connection token uuid
provider:
type: integer
provider_obj:
@@ -31793,7 +31792,6 @@ components:
endpoint:
type: string
format: uuid
- readOnly: true
endpoint_obj:
allOf:
- $ref: '#/components/schemas/Endpoint'
@@ -31805,7 +31803,6 @@ components:
required:
- endpoint
- endpoint_obj
- - pk
- provider
- provider_obj
- user
@@ -31813,9 +31810,17 @@ components:
type: object
description: ConnectionToken Serializer
properties:
+ pk:
+ type: string
+ format: uuid
+ title: Connection token uuid
provider:
type: integer
+ endpoint:
+ type: string
+ format: uuid
required:
+ - endpoint
- provider
ConsentChallenge:
type: object
@@ -34332,6 +34337,8 @@ components:
type: boolean
application_pre:
type: string
+ flow_designation:
+ $ref: '#/components/schemas/FlowDesignationEnum'
enroll_url:
type: string
recovery_url:
@@ -34347,6 +34354,7 @@ components:
show_source_labels:
type: boolean
required:
+ - flow_designation
- password_fields
- primary_action
- show_source_labels
@@ -38586,8 +38594,15 @@ components:
type: object
description: ConnectionToken Serializer
properties:
+ pk:
+ type: string
+ format: uuid
+ title: Connection token uuid
provider:
type: integer
+ endpoint:
+ type: string
+ format: uuid
PatchedConsentStageRequest:
type: object
description: ConsentStage Serializer
@@ -45584,8 +45599,8 @@ components:
description: Get latest version from cache
readOnly: true
version_latest_valid:
- type: boolean
- description: Latest version query is a valid non-default value
+ type: string
+ description: Check if latest version is valid
readOnly: true
build_hash:
type: string
diff --git a/web/src/admin/providers/rac/ConnectionTokenList.ts b/web/src/admin/providers/rac/ConnectionTokenList.ts
index 4161cedaee..d9d4fbf830 100644
--- a/web/src/admin/providers/rac/ConnectionTokenList.ts
+++ b/web/src/admin/providers/rac/ConnectionTokenList.ts
@@ -61,12 +61,12 @@ export class ConnectionTokenListPage extends Table {
}}
.usedBy=${(item: ConnectionToken) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensUsedByList({
- connectionTokenUuid: item.pk,
+ connectionTokenUuid: item.pk || "",
});
}}
.delete=${(item: ConnectionToken) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensDestroy({
- connectionTokenUuid: item.pk,
+ connectionTokenUuid: item.pk || "",
});
}}
>
diff --git a/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.stories.ts b/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.stories.ts
new file mode 100644
index 0000000000..24a4f4a4a8
--- /dev/null
+++ b/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.stories.ts
@@ -0,0 +1,58 @@
+import type { StoryObj } from "@storybook/web-components";
+
+import { html } from "lit";
+
+import "@patternfly/patternfly/components/Login/login.css";
+
+import { AuthenticatorTOTPChallenge, ChallengeChoices, UiThemeEnum } from "@goauthentik/api";
+
+import "../../../stories/flow-interface";
+import "./AuthenticatorTOTPStage";
+
+export default {
+ title: "Flow / Stages / AuthenticatorTOTPStage",
+};
+
+export const LoadingNoChallenge = () => {
+ return html`
+
+ `;
+};
+
+export const Challenge: StoryObj = {
+ render: ({ theme, challenge }) => {
+ return html`
+ `;
+ },
+ args: {
+ theme: "automatic",
+ challenge: {
+ type: ChallengeChoices.Native,
+ pendingUser: "foo",
+ pendingUserAvatar: "https://picsum.photos/64",
+ configUrl: "",
+ } as AuthenticatorTOTPChallenge,
+ },
+ argTypes: {
+ theme: {
+ options: [UiThemeEnum.Automatic, UiThemeEnum.Light, UiThemeEnum.Dark],
+ control: {
+ type: "select",
+ },
+ },
+ },
+};
diff --git a/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts b/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts
index 38030c10a0..074adb29ca 100644
--- a/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts
+++ b/web/src/flow/stages/authenticator_totp/AuthenticatorTOTPStage.ts
@@ -106,6 +106,11 @@ export class AuthenticatorTOTPStage extends BaseStage<
+
+ ${msg(
+ "Please scan the QR code above using the Microsoft Authenticator, Google Authenticator, or other authenticator apps on your device, and enter the code the device displays below to finish setting up the MFA device.",
+ )}
+
uiFields[f]));
- return html`
+ ${msg(
+ "Enter the email associated with your account, and we'll send you a link to reset your password.",
+ )}
+
+ `
+ : nothing}
+