From 20b3c6bfc2fb234dfcbdf3520dffdfaebb6ed135 Mon Sep 17 00:00:00 2001 From: peintnermax Date: Tue, 15 Oct 2024 17:27:08 +0200 Subject: [PATCH] set password form --- .../src/app/(login)/password/set/page.tsx | 9 +- apps/login/src/app/(login)/register/page.tsx | 6 +- .../src/components/set-password-form.tsx | 19 +- .../components/set-register-password-form.tsx | 197 ++++++++++++++++++ apps/login/src/lib/server/password.ts | 22 ++ 5 files changed, 227 insertions(+), 26 deletions(-) create mode 100644 apps/login/src/components/set-register-password-form.tsx diff --git a/apps/login/src/app/(login)/password/set/page.tsx b/apps/login/src/app/(login)/password/set/page.tsx index d6c87850ebb..3be72f46dc5 100644 --- a/apps/login/src/app/(login)/password/set/page.tsx +++ b/apps/login/src/app/(login)/password/set/page.tsx @@ -1,10 +1,9 @@ import { Alert } from "@/components/alert"; import { DynamicTheme } from "@/components/dynamic-theme"; -import { PasswordForm } from "@/components/password-form"; +import { SetPasswordForm } from "@/components/set-password-form"; import { UserAvatar } from "@/components/user-avatar"; import { loadMostRecentSession } from "@/lib/session"; import { getBrandingSettings, getLoginSettings } from "@/lib/zitadel"; -import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; import { getLocale, getTranslations } from "next-intl/server"; export default async function Page({ @@ -58,15 +57,11 @@ export default async function Page({ )} {loginName && ( - )} diff --git a/apps/login/src/app/(login)/register/page.tsx b/apps/login/src/app/(login)/register/page.tsx index 8169d205a0f..ad84e81b312 100644 --- a/apps/login/src/app/(login)/register/page.tsx +++ b/apps/login/src/app/(login)/register/page.tsx @@ -1,6 +1,6 @@ import { DynamicTheme } from "@/components/dynamic-theme"; import { RegisterFormWithoutPassword } from "@/components/register-form-without-password"; -import { SetPasswordForm } from "@/components/set-password-form"; +import { SetRegisterPasswordForm } from "@/components/set-register-password-form"; import { getBrandingSettings, getLegalAndSupportSettings, @@ -38,14 +38,14 @@ export default async function Page({

{t("description")}

{legal && passwordComplexitySettings && ( - + > )} diff --git a/apps/login/src/components/set-password-form.tsx b/apps/login/src/components/set-password-form.tsx index 507393c1a97..e865bfd4796 100644 --- a/apps/login/src/components/set-password-form.tsx +++ b/apps/login/src/components/set-password-form.tsx @@ -6,7 +6,7 @@ import { symbolValidator, upperCaseValidator, } from "@/helpers/validators"; -import { registerUser, RegisterUserResponse } from "@/lib/server/register"; +import { RegisterUserResponse } from "@/lib/server/register"; import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb"; import { useTranslations } from "next-intl"; import { useRouter } from "next/navigation"; @@ -28,18 +28,12 @@ type Inputs = type Props = { passwordComplexitySettings: PasswordComplexitySettings; - email: string; - firstname: string; - lastname: string; organization?: string; authRequestId?: string; }; export function SetPasswordForm({ passwordComplexitySettings, - email, - firstname, - lastname, organization, authRequestId, }: Props) { @@ -47,11 +41,7 @@ export function SetPasswordForm({ const { register, handleSubmit, watch, formState } = useForm({ mode: "onBlur", - defaultValues: { - email: email ?? "", - firstname: firstname ?? "", - lastname: lastname ?? "", - }, + defaultValues: {}, }); const [loading, setLoading] = useState(false); @@ -61,10 +51,7 @@ export function SetPasswordForm({ async function submitRegister(values: Inputs) { setLoading(true); - const response = await registerUser({ - email: email, - firstName: firstname, - lastName: lastname, + const response = await changePassword({ organization: organization, authRequestId: authRequestId, password: values.password, diff --git a/apps/login/src/components/set-register-password-form.tsx b/apps/login/src/components/set-register-password-form.tsx new file mode 100644 index 00000000000..b8f2eaf3c53 --- /dev/null +++ b/apps/login/src/components/set-register-password-form.tsx @@ -0,0 +1,197 @@ +"use client"; + +import { + lowerCaseValidator, + numberValidator, + symbolValidator, + upperCaseValidator, +} from "@/helpers/validators"; +import { registerUser, RegisterUserResponse } from "@/lib/server/register"; +import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { FieldValues, useForm } from "react-hook-form"; +import { Alert } from "./alert"; +import { BackButton } from "./back-button"; +import { Button, ButtonVariants } from "./button"; +import { TextInput } from "./input"; +import { PasswordComplexity } from "./password-complexity"; +import { Spinner } from "./spinner"; + +type Inputs = + | { + password: string; + confirmPassword: string; + } + | FieldValues; + +type Props = { + passwordComplexitySettings: PasswordComplexitySettings; + email: string; + firstname: string; + lastname: string; + organization?: string; + authRequestId?: string; +}; + +export function SetRegisterPasswordForm({ + passwordComplexitySettings, + email, + firstname, + lastname, + organization, + authRequestId, +}: Props) { + const t = useTranslations("register"); + + const { register, handleSubmit, watch, formState } = useForm({ + mode: "onBlur", + defaultValues: { + email: email ?? "", + firstname: firstname ?? "", + lastname: lastname ?? "", + }, + }); + + const [loading, setLoading] = useState(false); + const [error, setError] = useState(""); + + const router = useRouter(); + + async function submitRegister(values: Inputs) { + setLoading(true); + const response = await registerUser({ + email: email, + firstName: firstname, + lastName: lastname, + organization: organization, + authRequestId: authRequestId, + password: values.password, + }).catch(() => { + setError("Could not register user"); + }); + + if (response && "error" in response) { + setError(response.error); + } + + setLoading(false); + + if (!response) { + setError("Could not register user"); + return; + } + + const userResponse = response as RegisterUserResponse; + + const params = new URLSearchParams({ userId: userResponse.userId }); + + if (userResponse.factors?.user?.loginName) { + params.append("loginName", userResponse.factors.user.loginName); + } + if (organization) { + params.append("organization", organization); + } + if (userResponse && userResponse.sessionId) { + params.append("sessionId", userResponse.sessionId); + } + + // skip verification for now as it is an app based flow + // return router.push(`/verify?` + params); + + // check for mfa force to continue with mfa setup + + if (authRequestId && userResponse.sessionId) { + if (authRequestId) { + params.append("authRequest", authRequestId); + } + return router.push(`/login?` + params); + } else { + if (authRequestId) { + params.append("authRequestId", authRequestId); + } + return router.push(`/signedin?` + params); + } + } + + const { errors } = formState; + + const watchPassword = watch("password", ""); + const watchConfirmPassword = watch("confirmPassword", ""); + + const hasMinLength = + passwordComplexitySettings && + watchPassword?.length >= passwordComplexitySettings.minLength; + const hasSymbol = symbolValidator(watchPassword); + const hasNumber = numberValidator(watchPassword); + const hasUppercase = upperCaseValidator(watchPassword); + const hasLowercase = lowerCaseValidator(watchPassword); + + const policyIsValid = + passwordComplexitySettings && + (passwordComplexitySettings.requiresLowercase ? hasLowercase : true) && + (passwordComplexitySettings.requiresNumber ? hasNumber : true) && + (passwordComplexitySettings.requiresUppercase ? hasUppercase : true) && + (passwordComplexitySettings.requiresSymbol ? hasSymbol : true) && + hasMinLength; + + return ( +
+
+
+ +
+
+ +
+
+ + {passwordComplexitySettings && ( + + )} + + {error && {error}} + +
+ + +
+ + ); +} diff --git a/apps/login/src/lib/server/password.ts b/apps/login/src/lib/server/password.ts index 2a083806581..d5ca8c671e1 100644 --- a/apps/login/src/lib/server/password.ts +++ b/apps/login/src/lib/server/password.ts @@ -107,3 +107,25 @@ export async function sendPassword(command: UpdateSessionCommand) { authMethods, }; } + +export async function changePassword(command: { + userId: string; + password: string; +}) { + // check for init state + const users = await listUsers({ + loginName: command.loginName, + organizationId: command.organization, + }); + + if ( + !users.details || + users.details.totalResult !== BigInt(1) || + !users.result[0].userId + ) { + return { error: "Could not send Password Reset Link" }; + } + const userId = users.result[0].userId; + + return passwordReset(userId); +}