From 9d975bb39a9889eef504ae33d124d53dcc635986 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Mon, 26 May 2025 15:48:54 +0200 Subject: [PATCH] ldap components --- apps/login/src/app/(login)/password/page.tsx | 7 +- apps/login/src/components/login-passkey.tsx | 12 +- apps/login/src/components/password-form.tsx | 4 - .../src/components/username-password-form.tsx | 120 ++++++++++++++++++ 4 files changed, 127 insertions(+), 16 deletions(-) create mode 100644 apps/login/src/components/username-password-form.tsx diff --git a/apps/login/src/app/(login)/password/page.tsx b/apps/login/src/app/(login)/password/page.tsx index 506454a275..29a49c61f2 100644 --- a/apps/login/src/app/(login)/password/page.tsx +++ b/apps/login/src/app/(login)/password/page.tsx @@ -10,7 +10,6 @@ import { getLoginSettings, } from "@/lib/zitadel"; import { Organization } from "@zitadel/proto/zitadel/org/v2/org_pb"; -import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; import { getLocale, getTranslations } from "next-intl/server"; import { headers } from "next/headers"; @@ -22,7 +21,7 @@ export default async function Page(props: { const t = await getTranslations({ locale, namespace: "password" }); const tError = await getTranslations({ locale, namespace: "error" }); - let { loginName, organization, requestId, alt } = searchParams; + let { loginName, organization, requestId } = searchParams; const _headers = await headers(); const { serviceUrl } = getServiceUrlFromHeaders(_headers); @@ -93,10 +92,6 @@ export default async function Page(props: { requestId={requestId} organization={organization} // stick to "organization" as we still want to do user discovery based on the searchParams not the default organization, later the organization is determined by the found user loginSettings={loginSettings} - promptPasswordless={ - loginSettings?.passkeysType == PasskeysType.ALLOWED - } - isAlternative={alt === "true"} /> )} diff --git a/apps/login/src/components/login-passkey.tsx b/apps/login/src/components/login-passkey.tsx index b3f0b1212f..b364a9de45 100644 --- a/apps/login/src/components/login-passkey.tsx +++ b/apps/login/src/components/login-passkey.tsx @@ -210,26 +210,26 @@ export function LoginPasskey({ type="button" variant={ButtonVariants.Secondary} onClick={() => { - const params: any = { alt: "true" }; + const params = new URLSearchParams(); if (loginName) { - params.loginName = loginName; + params.append("loginName", loginName); } if (sessionId) { - params.sessionId = sessionId; + params.append("sessionId", sessionId); } if (requestId) { - params.requestId = requestId; + params.append("requestId", requestId); } if (organization) { - params.organization = organization; + params.append("organization", organization); } return router.push( - "/password?" + new URLSearchParams(params), // alt is set because password is requested as alternative auth method, so passwordless prompt can be escaped + "/password?" + params, // alt is set because password is requested as alternative auth method, so passwordless prompt can be escaped ); }} data-testid="password-button" diff --git a/apps/login/src/components/password-form.tsx b/apps/login/src/components/password-form.tsx index 17461644d8..a9b0a01316 100644 --- a/apps/login/src/components/password-form.tsx +++ b/apps/login/src/components/password-form.tsx @@ -23,8 +23,6 @@ type Props = { loginName: string; organization?: string; requestId?: string; - isAlternative?: boolean; // whether password was requested as alternative auth method - promptPasswordless?: boolean; }; export function PasswordForm({ @@ -32,8 +30,6 @@ export function PasswordForm({ loginName, organization, requestId, - promptPasswordless, - isAlternative, }: Props) { const t = useTranslations("password"); diff --git a/apps/login/src/components/username-password-form.tsx b/apps/login/src/components/username-password-form.tsx new file mode 100644 index 0000000000..fb6a98d8c3 --- /dev/null +++ b/apps/login/src/components/username-password-form.tsx @@ -0,0 +1,120 @@ +"use client"; + +import { sendPassword } from "@/lib/server/password"; +import { create } from "@zitadel/client"; +import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb"; +import { LoginSettings } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb"; +import { useTranslations } from "next-intl"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { Alert } from "./alert"; +import { BackButton } from "./back-button"; +import { Button, ButtonVariants } from "./button"; +import { TextInput } from "./input"; +import { Spinner } from "./spinner"; + +type Inputs = { + loginName: string; + password: string; +}; + +type Props = { + loginSettings: LoginSettings | undefined; + loginName: string; + organization?: string; + requestId?: string; +}; + +export function UsernamePasswordForm({ + loginSettings, + loginName, + organization, + requestId, +}: Props) { + const t = useTranslations("password"); + + const { register, handleSubmit, formState } = useForm({ + mode: "onBlur", + }); + + const [error, setError] = useState(""); + + const [loading, setLoading] = useState(false); + + const router = useRouter(); + + async function submitUsernamePassword(values: Inputs) { + setError(""); + setLoading(true); + + const response = await sendPassword({ + loginName, + organization, + checks: create(ChecksSchema, { + password: { password: values.password }, + }), + requestId, + }) + .catch(() => { + setError("Could not verify password"); + return; + }) + .finally(() => { + setLoading(false); + }); + + if (response && "error" in response && response.error) { + setError(response.error); + return; + } + + if (response && "redirect" in response && response.redirect) { + return router.push(response.redirect); + } + } + + return ( +
+ + +
+ +
+ + {error && ( +
+ {error} +
+ )} + +
+ + + +
+ + ); +}