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} +