redirect to /set

This commit is contained in:
peintnermax
2024-10-16 16:29:48 +02:00
parent 94bd6bc3f6
commit 69bc1c19e3
10 changed files with 139 additions and 47 deletions

View File

@@ -14,10 +14,19 @@
"register": "Neuen Benutzer registrieren" "register": "Neuen Benutzer registrieren"
}, },
"password": { "password": {
"title": "Passwort", "verify": {
"description": "Geben Sie Ihr Passwort ein.", "title": "Passwort",
"resetPassword": "Passwort zurücksetzen", "description": "Geben Sie Ihr Passwort ein.",
"submit": "Weiter" "resetPassword": "Passwort zurücksetzen",
"submit": "Weiter"
},
"set": {
"title": "Passwort festlegen",
"description": "Legen Sie das Passwort für Ihr Konto fest",
"codeSent": "Ein Code wurde an Ihre E-Mail-Adresse gesendet.",
"resend": "Erneut senden",
"submit": "Weiter"
}
}, },
"idp": { "idp": {
"title": "Mit SSO anmelden", "title": "Mit SSO anmelden",

View File

@@ -14,10 +14,19 @@
"register": "Register new user" "register": "Register new user"
}, },
"password": { "password": {
"title": "Password", "verify": {
"description": "Enter your password.", "title": "Password",
"resetPassword": "Reset Password", "description": "Enter your password.",
"submit": "Continue" "resetPassword": "Reset Password",
"submit": "Continue"
},
"set": {
"title": "Set Password",
"description": "Set the password for your account",
"codeSent": "A code has been sent to your email address.",
"resend": "Resend code",
"submit": "Continue"
}
}, },
"idp": { "idp": {
"title": "Sign in with SSO", "title": "Sign in with SSO",

View File

@@ -14,10 +14,19 @@
"register": "Registrar nuevo usuario" "register": "Registrar nuevo usuario"
}, },
"password": { "password": {
"title": "Contraseña", "verify": {
"description": "Introduce tu contraseña.", "title": "Contraseña",
"resetPassword": "Restablecer Contraseña", "description": "Introduce tu contraseña.",
"submit": "Continuar" "resetPassword": "Restablecer contraseña",
"submit": "Continuar"
},
"set": {
"title": "Establecer Contraseña",
"description": "Establece la contraseña para tu cuenta",
"codeSent": "Se ha enviado un código a su correo electrónico.",
"resend": "Reenviar código",
"submit": "Continuar"
}
}, },
"idp": { "idp": {
"title": "Iniciar sesión con SSO", "title": "Iniciar sesión con SSO",

View File

@@ -14,10 +14,19 @@
"register": "Registrati come nuovo utente" "register": "Registrati come nuovo utente"
}, },
"password": { "password": {
"title": "Password", "verify": {
"description": "Inserisci la tua password.", "title": "Password",
"resetPassword": "Reimposta Password", "description": "Inserisci la tua password.",
"submit": "Continua" "resetPassword": "Reimposta Password",
"submit": "Continua"
},
"set": {
"title": "Imposta Password",
"description": "Imposta la password per il tuo account",
"codeSent": "Un codice è stato inviato al tuo indirizzo email.",
"resend": "Invia di nuovo",
"submit": "Continua"
}
}, },
"idp": { "idp": {
"title": "Accedi con SSO", "title": "Accedi con SSO",

View File

@@ -32,13 +32,13 @@ export default async function Page({
const branding = await getBrandingSettings(organization); const branding = await getBrandingSettings(organization);
const loginSettings = await getLoginSettings(organization); const loginSettings = await getLoginSettings(organization);
console.log(sessionFactors);
return ( return (
<DynamicTheme branding={branding}> <DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<h1>{sessionFactors?.factors?.user?.displayName ?? t("title")}</h1> <h1>
<p className="ztdl-p mb-6 block">{t("description")}</p> {sessionFactors?.factors?.user?.displayName ?? t("verify.title")}
</h1>
<p className="ztdl-p mb-6 block">{t("verify.description")}</p>
{/* show error only if usernames should be shown to be unknown */} {/* show error only if usernames should be shown to be unknown */}
{(!sessionFactors || !loginName) && {(!sessionFactors || !loginName) &&

View File

@@ -1,4 +1,4 @@
import { Alert } from "@/components/alert"; import { Alert, AlertType } from "@/components/alert";
import { DynamicTheme } from "@/components/dynamic-theme"; import { DynamicTheme } from "@/components/dynamic-theme";
import { SetPasswordForm } from "@/components/set-password-form"; import { SetPasswordForm } from "@/components/set-password-form";
import { UserAvatar } from "@/components/user-avatar"; import { UserAvatar } from "@/components/user-avatar";
@@ -37,8 +37,8 @@ export default async function Page({
return ( return (
<DynamicTheme branding={branding}> <DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
<h1>{sessionFactors?.factors?.user?.displayName ?? t("title")}</h1> <h1>{sessionFactors?.factors?.user?.displayName ?? t("set.title")}</h1>
<p className="ztdl-p mb-6 block">{t("description")}</p> <p className="ztdl-p mb-6 block">{t("set.description")}</p>
{/* show error only if usernames should be shown to be unknown */} {/* show error only if usernames should be shown to be unknown */}
{(!sessionFactors || !loginName) && {(!sessionFactors || !loginName) &&
@@ -57,9 +57,14 @@ export default async function Page({
></UserAvatar> ></UserAvatar>
)} )}
{passwordComplexity && loginName ? ( <Alert type={AlertType.INFO}>{t("set.codeSent")}</Alert>
{passwordComplexity &&
loginName &&
sessionFactors?.factors?.user?.id ? (
<SetPasswordForm <SetPasswordForm
code={code} code={code}
userId={sessionFactors.factors.user.id}
loginName={loginName} loginName={loginName}
authRequestId={authRequestId} authRequestId={authRequestId}
organization={organization} organization={organization}

View File

@@ -87,13 +87,25 @@ export function PasswordForm({
if (response && "error" in response) { if (response && "error" in response) {
setError(response.error); setError(response.error);
} else { return;
setInfo("Password was reset. Please check your email.");
} }
setLoading(false); setInfo("Password was reset. Please check your email.");
return response; setLoading(false);
const params = new URLSearchParams({
loginName: loginName,
});
if (organization) {
params.append("organization", organization);
}
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
return router.push("/password/set?" + params);
} }
async function submitPasswordAndContinue( async function submitPasswordAndContinue(
@@ -247,7 +259,7 @@ export function PasswordForm({
type="button" type="button"
disabled={loading} disabled={loading}
> >
{t("resetPassword")} {t("verify.resetPassword")}
</button> </button>
)} )}
@@ -284,7 +296,7 @@ export function PasswordForm({
onClick={handleSubmit(submitPasswordAndContinue)} onClick={handleSubmit(submitPasswordAndContinue)}
> >
{loading && <Spinner className="h-5 w-5 mr-2" />} {loading && <Spinner className="h-5 w-5 mr-2" />}
{t("submit")} {t("verify.submit")}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -7,8 +7,8 @@ import {
upperCaseValidator, upperCaseValidator,
} from "@/helpers/validators"; } from "@/helpers/validators";
import { changePassword } from "@/lib/server/password"; import { changePassword } from "@/lib/server/password";
import { RegisterUserResponse } from "@/lib/server/register";
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb"; import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
import { SetPasswordResponse } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
@@ -22,6 +22,7 @@ import { Spinner } from "./spinner";
type Inputs = type Inputs =
| { | {
code: string;
password: string; password: string;
confirmPassword: string; confirmPassword: string;
} }
@@ -31,6 +32,7 @@ type Props = {
code?: string; code?: string;
passwordComplexitySettings: PasswordComplexitySettings; passwordComplexitySettings: PasswordComplexitySettings;
loginName: string; loginName: string;
userId: string;
organization?: string; organization?: string;
authRequestId?: string; authRequestId?: string;
}; };
@@ -39,12 +41,17 @@ export function SetPasswordForm({
passwordComplexitySettings, passwordComplexitySettings,
organization, organization,
authRequestId, authRequestId,
loginName,
userId,
code,
}: Props) { }: Props) {
const t = useTranslations("register"); const t = useTranslations("password");
const { register, handleSubmit, watch, formState } = useForm<Inputs>({ const { register, handleSubmit, watch, formState } = useForm<Inputs>({
mode: "onBlur", mode: "onBlur",
defaultValues: {}, defaultValues: {
code: code ?? "",
},
}); });
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
@@ -55,9 +62,9 @@ export function SetPasswordForm({
async function submitRegister(values: Inputs) { async function submitRegister(values: Inputs) {
setLoading(true); setLoading(true);
const response = await changePassword({ const response = await changePassword({
organization: organization, userId: userId,
authRequestId: authRequestId,
password: values.password, password: values.password,
code: values.code,
}).catch(() => { }).catch(() => {
setError("Could not register user"); setError("Could not register user");
}); });
@@ -73,19 +80,18 @@ export function SetPasswordForm({
return; return;
} }
const userResponse = response as RegisterUserResponse; const userResponse = response as SetPasswordResponse & {
sessionId: string;
};
const params = new URLSearchParams({ userId: userResponse.userId }); const params = new URLSearchParams({});
if (userResponse.factors?.user?.loginName) { if (loginName) {
params.append("loginName", userResponse.factors.user.loginName); params.append("loginName", loginName);
} }
if (organization) { if (organization) {
params.append("organization", 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 // skip verification for now as it is an app based flow
// return router.push(`/verify?` + params); // return router.push(`/verify?` + params);
@@ -129,6 +135,24 @@ export function SetPasswordForm({
return ( return (
<form className="w-full"> <form className="w-full">
<div className="pt-4 grid grid-cols-1 gap-4 mb-4"> <div className="pt-4 grid grid-cols-1 gap-4 mb-4">
<div className="flex flex-row items-end">
<div className="flex-1">
<TextInput
type="text"
required
{...register("code", {
required: "This field is required",
})}
label="Code"
error={errors.code?.message as string}
/>
</div>
<div className="ml-4 mb-1">
<Button variant={ButtonVariants.Secondary}>
{t("set.resend")}
</Button>
</div>
</div>
<div className=""> <div className="">
<TextInput <TextInput
type="password" type="password"
@@ -137,7 +161,7 @@ export function SetPasswordForm({
{...register("password", { {...register("password", {
required: "You have to provide a password!", required: "You have to provide a password!",
})} })}
label="Password" label="New Password"
error={errors.password?.message as string} error={errors.password?.message as string}
/> />
</div> </div>
@@ -179,7 +203,7 @@ export function SetPasswordForm({
onClick={handleSubmit(submitRegister)} onClick={handleSubmit(submitRegister)}
> >
{loading && <Spinner className="h-5 w-5 mr-2" />} {loading && <Spinner className="h-5 w-5 mr-2" />}
{t("password.submit")} {t("set.submit")}
</Button> </Button>
</div> </div>
</form> </form>

View File

@@ -111,7 +111,7 @@ export async function sendPassword(command: UpdateSessionCommand) {
} }
export async function changePassword(command: { export async function changePassword(command: {
code?: string; code: string;
userId: string; userId: string;
password: string; password: string;
}) { }) {
@@ -123,5 +123,5 @@ export async function changePassword(command: {
} }
const userId = user.userId; const userId = user.userId;
return setPassword(userId, command.password); return setPassword(userId, command.password, command.code);
} }

View File

@@ -28,6 +28,7 @@ import {
} from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
import { PasswordComplexitySettingsSchema } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb"; import { PasswordComplexitySettingsSchema } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
import type { RedirectURLsJson } from "@zitadel/proto/zitadel/user/v2/idp_pb"; import type { RedirectURLsJson } from "@zitadel/proto/zitadel/user/v2/idp_pb";
import { NotificationType } from "@zitadel/proto/zitadel/user/v2/password_pb";
import { import {
SearchQuery, SearchQuery,
SearchQuerySchema, SearchQuerySchema,
@@ -495,18 +496,32 @@ export async function passwordReset(userId: string) {
return userService.passwordReset( return userService.passwordReset(
{ {
userId, userId,
medium: {
case: "sendLink",
value: {
notificationType: NotificationType.Email,
},
},
}, },
{}, {},
); );
} }
export async function setPassword(userId: string, password: string) { export async function setPassword(
userId: string,
password: string,
code: string,
) {
return userService.setPassword( return userService.setPassword(
{ {
userId, userId,
newPassword: { newPassword: {
password, password,
}, },
verification: {
case: "verificationCode",
value: code,
},
}, },
{}, {},
); );