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 (
+
+ );
+}
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);
+}