Reviewing the email verification field and reset action (#44466)

(cherry picked from commit c4edb97e68688472e5cd58fdb30bdd193d721cd2)

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
Co-authored-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Martin Kanis 2025-11-28 13:39:02 +01:00 committed by GitHub
parent 56d2434105
commit 4a2700197f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 49 deletions

View File

@ -329,8 +329,12 @@ loginScreenCustomization=Login screen customization
policiesConfigType=Configure via\:
exportWarningTitle=Export with caution
emailVerifiedHelp=Has the user's email been verified?
emailPendingVerification=Email pending verification
emailPendingVerificationHelp=This field can only be cleared. Clearing it will invalidate the verification link.
emailPendingVerificationAlertTitle=Email pending verification
emailPendingVerificationResetAction=Click here to keep the current email.
emailPendingVerificationActionMessage=This action will remove the email pending verification for this user.
confirmEmailPendingVerificationAction=Keep current email
emailPendingVerificationUpdateError=Could not unset email pending verification.
userNotYetConfirmedNewEmail=The user has not confirmed the new email address {{email}}.
duplicateFlow=Duplicate flow
addExecution=Add execution
noSearchResultsInstructions=Click on the search bar above to search again

View File

@ -1,6 +1,9 @@
import type GroupRepresentation from "@keycloak/keycloak-admin-client/lib/defs/groupRepresentation";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { UserProfileMetadata } from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
import {
UserProfileAttributeMetadata,
UserProfileMetadata,
} from "@keycloak/keycloak-admin-client/lib/defs/userProfileMetadata";
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation";
import {
FormErrorText,
@ -8,9 +11,10 @@ import {
SwitchControl,
TextControl,
UserProfileFields,
beerify,
ContinueCancelModal,
} from "@keycloak/keycloak-ui-shared";
import {
Alert,
AlertVariant,
Button,
Chip,
@ -22,7 +26,7 @@ import {
TextInput,
} from "@patternfly/react-core";
import { TFunction } from "i18next";
import { useCallback, useEffect, useState } from "react";
import { useEffect, useState } from "react";
import { Controller, FormProvider, UseFormReturn } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useAdminClient } from "../admin-client";
@ -151,45 +155,32 @@ export const UserForm = ({
?.map((a) => a.readOnly)
.reduce((p, c) => p && c, true);
const validateAndSave = useCallback(
(formData: UserFormFields) => {
const originalEmailPendingValue =
user?.attributes?.["kc.email.pending"]?.[0];
if (
formData.attributes &&
!Array.isArray(formData.attributes) &&
originalEmailPendingValue
) {
const emailPendingValue = (
formData.attributes as Record<string, string | string[]>
)[beerify("kc.email.pending")];
const currentValue = Array.isArray(emailPendingValue)
? emailPendingValue[0]
: emailPendingValue;
if (currentValue === "") {
// Field was cleared - keep as empty string to clear the value
(formData.attributes as Record<string, string | string[]>)[
beerify("kc.email.pending")
] = "";
} else if (currentValue && currentValue !== originalEmailPendingValue) {
// Field was modified (not cleared) - revert to original value
(formData.attributes as Record<string, string | string[]>)[
beerify("kc.email.pending")
] = originalEmailPendingValue;
}
const handleEmailVerificationReset = async () => {
try {
save(
toUserFormFields({
...user,
requiredActions: user?.requiredActions?.filter(
(action) => action !== "UPDATE_EMAIL",
),
attributes: {
...user?.attributes,
"kc.email.pending": "",
},
}),
);
if (refresh) {
refresh();
}
save(formData);
},
[save, user],
);
} catch (error) {
addError("emailPendingVerificationUpdateError", error);
}
};
return (
<FormAccess
isHorizontal
onSubmit={handleSubmit(validateAndSave)}
onSubmit={handleSubmit(save)}
role="query-users"
fineGrainedAccess={user?.access?.manage}
className="pf-v5-u-mt-lg"
@ -276,20 +267,36 @@ export const UserForm = ({
label={t("emailVerified")}
labelIcon={t("emailVerifiedHelp")}
/>
{user?.attributes?.["kc.email.pending"] && (
<Alert
variant={AlertVariant.warning}
isInline
isPlain
title={t("emailPendingVerificationAlertTitle")}
>
{t("userNotYetConfirmedNewEmail", {
email: user.attributes!["kc.email.pending"],
})}
<ContinueCancelModal
buttonTitle={t("emailPendingVerificationResetAction")}
modalTitle={t("confirmEmailPendingVerificationAction")}
continueLabel={t("confirm")}
cancelLabel={t("cancel")}
buttonVariant="link"
onContinue={handleEmailVerificationReset}
>
{t("emailPendingVerificationActionMessage")}
</ContinueCancelModal>
</Alert>
)}
<UserProfileFields
form={form}
userProfileMetadata={{
...userProfileMetadata,
attributes: userProfileMetadata.attributes?.map((attr) =>
attr.name === "kc.email.pending"
? {
...attr,
annotations: {
...attr.annotations,
inputHelperTextBefore: "emailPendingVerificationHelp",
},
}
: attr,
attributes: userProfileMetadata.attributes?.filter(
(attribute: UserProfileAttributeMetadata) => {
return attribute.name !== "kc.email.pending";
},
),
}}
hideReadOnly={!user}