diff --git a/apps/login/src/app/(login)/mfa/set/page.tsx b/apps/login/src/app/(login)/mfa/set/page.tsx index 11c44a22fa..c7f2fa6599 100644 --- a/apps/login/src/app/(login)/mfa/set/page.tsx +++ b/apps/login/src/app/(login)/mfa/set/page.tsx @@ -134,7 +134,6 @@ export default async function Page(props: { {!(loginName || sessionId) && {tError("unknownContext")}} - {/* this happens if you register a user and open up the email verification link on a different device than the device where the registration was made. */} {!valid && {tError("sessionExpired")}} {isSessionValid(sessionWithData).valid && diff --git a/apps/login/src/app/(login)/verify/page.tsx b/apps/login/src/app/(login)/verify/page.tsx index 43aff0242b..7634ff063a 100644 --- a/apps/login/src/app/(login)/verify/page.tsx +++ b/apps/login/src/app/(login)/verify/page.tsx @@ -45,7 +45,6 @@ export default async function Page(props: { searchParams: Promise }) { if (invite === "true") { await sendInviteEmailCode({ - serviceUrl, userId, urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` + @@ -56,7 +55,6 @@ export default async function Page(props: { searchParams: Promise }) { }); } else { await sendEmailCode({ - serviceUrl, userId, urlTemplate: `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` + diff --git a/apps/login/src/components/verify-redirect-button.tsx b/apps/login/src/components/verify-redirect-button.tsx deleted file mode 100644 index c968da86df..0000000000 --- a/apps/login/src/components/verify-redirect-button.tsx +++ /dev/null @@ -1,102 +0,0 @@ -"use client"; - -import { - sendVerificationRedirectWithoutCheck, - SendVerificationRedirectWithoutCheckCommand, -} from "@/lib/server/verify"; -import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb"; -import { useTranslations } from "next-intl"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { Alert, AlertType } from "./alert"; -import { BackButton } from "./back-button"; -import { Button, ButtonVariants } from "./button"; -import { Spinner } from "./spinner"; - -export function VerifyRedirectButton({ - userId, - loginName, - requestId, - authMethods, - organization, -}: { - userId?: string; - loginName?: string; - requestId: string; - authMethods: AuthenticationMethodType[] | null; - organization?: string; -}) { - const t = useTranslations("verify"); - const [error, setError] = useState(""); - - const [loading, setLoading] = useState(false); - const router = useRouter(); - - async function submitAndContinue(): Promise { - setLoading(true); - - let command = { - organization, - requestId, - } as SendVerificationRedirectWithoutCheckCommand; - - if (userId) { - command = { - ...command, - userId, - } as SendVerificationRedirectWithoutCheckCommand; - } else if (loginName) { - command = { - ...command, - loginName, - } as SendVerificationRedirectWithoutCheckCommand; - } - - const response = await sendVerificationRedirectWithoutCheck(command) - .catch(() => { - setError("Could not verify"); - return; - }) - .finally(() => { - setLoading(false); - }); - - if (response && "error" in response && response.error) { - setError(response.error); - return; - } - - if (response && "redirect" in response && response.redirect) { - router.push(response.redirect); - return true; - } - } - - return ( - <> - {t("success")} - - {error && ( -
- {error} -
- )} - -
- - - {authMethods?.length === 0 && ( - - )} -
- - ); -} diff --git a/apps/login/src/lib/server/verify.ts b/apps/login/src/lib/server/verify.ts index 5591da7290..cf60f739b3 100644 --- a/apps/login/src/lib/server/verify.ts +++ b/apps/login/src/lib/server/verify.ts @@ -6,7 +6,6 @@ import { getSession, getUserByID, listAuthenticationMethodTypes, - resendInviteCode, verifyEmail, verifyInviteCode, verifyTOTPRegistration, @@ -17,7 +16,6 @@ import crypto from "crypto"; import { create } from "@zitadel/client"; import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb"; import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb"; -import { User } from "@zitadel/proto/zitadel/user/v2/user_pb"; import { cookies, headers } from "next/headers"; import { getNextUrl } from "../client"; import { getSessionCookieByLoginName } from "../cookies"; @@ -282,16 +280,19 @@ export async function resendVerification(command: resendVerifyEmailCommand) { const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; return command.isInvite - ? resendInviteCode({ + ? createInviteCode({ serviceUrl, userId: command.userId, + urlTemplate: + `${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` + + (command.requestId ? `&requestId=${command.requestId}` : ""), }).catch((error) => { if (error.code === 9) { return { error: "User is already verified!" }; } return { error: "Could not resend invite" }; }) - : sendEmailCode({ + : zitadelSendEmailCode({ userId: command.userId, serviceUrl, urlTemplate: @@ -300,191 +301,29 @@ export async function resendVerification(command: resendVerifyEmailCommand) { }); } -type sendEmailCommand = { - serviceUrl: string; +type SendEmailCommand = { userId: string; urlTemplate: string; }; -export async function sendEmailCode(command: sendEmailCommand) { - return zitadelSendEmailCode({ - serviceUrl: command.serviceUrl, - userId: command.userId, - urlTemplate: command.urlTemplate, - }); -} - -export async function sendInviteEmailCode(command: sendEmailCommand) { - // TODO: change this to sendInvite - return createInviteCode({ - serviceUrl: command.serviceUrl, - userId: command.userId, - urlTemplate: command.urlTemplate, - }); -} - -export type SendVerificationRedirectWithoutCheckCommand = { - organization?: string; - requestId?: string; -} & ( - | { userId: string; loginName?: never } - | { userId?: never; loginName: string } -); - -export async function sendVerificationRedirectWithoutCheck( - command: SendVerificationRedirectWithoutCheckCommand, -) { +export async function sendEmailCode(command: SendEmailCommand) { const _headers = await headers(); const { serviceUrl } = getServiceUrlFromHeaders(_headers); - if (!("loginName" in command || "userId" in command)) { - return { error: "No userId, nor loginname provided" }; - } - - let session: Session | undefined; - let user: User | undefined; - - if ("loginName" in command) { - const sessionCookie = await getSessionCookieByLoginName({ - loginName: command.loginName, - organization: command.organization, - }).catch((error) => { - console.warn("Ignored error:", error); - }); - - if (!sessionCookie) { - return { error: "Could not load session cookie" }; - } - - session = await getSession({ - serviceUrl, - sessionId: sessionCookie.id, - sessionToken: sessionCookie.token, - }).then((response) => { - if (response?.session) { - return response.session; - } - }); - - if (!session?.factors?.user?.id) { - return { error: "Could not create session for user" }; - } - - const userResponse = await getUserByID({ - serviceUrl, - userId: session?.factors?.user?.id, - }); - - if (!userResponse?.user) { - return { error: "Could not load user" }; - } - - user = userResponse.user; - } else if ("userId" in command) { - const userResponse = await getUserByID({ - serviceUrl, - userId: command.userId, - }); - - if (!userResponse?.user) { - return { error: "Could not load user" }; - } - - user = userResponse.user; - - const checks = create(ChecksSchema, { - user: { - search: { - case: "loginName", - value: userResponse.user.preferredLoginName, - }, - }, - }); - - session = await createSessionAndUpdateCookie({ - checks, - requestId: command.requestId, - }); - } - - if (!session?.factors?.user?.id) { - return { error: "Could not create session for user" }; - } - - if (!session?.factors?.user?.id) { - return { error: "Could not create session for user" }; - } - - if (!user) { - return { error: "Could not load user" }; - } - - const authMethodResponse = await listAuthenticationMethodTypes({ + return zitadelSendEmailCode({ serviceUrl, - userId: user.userId, + userId: command.userId, + urlTemplate: command.urlTemplate, + }); +} + +export async function sendInviteEmailCode(command: SendEmailCommand) { + const _headers = await headers(); + const { serviceUrl } = getServiceUrlFromHeaders(_headers); + + return createInviteCode({ + serviceUrl, + userId: command.userId, + urlTemplate: command.urlTemplate, }); - - if (!authMethodResponse || !authMethodResponse.authMethodTypes) { - return { error: "Could not load possible authenticators" }; - } - - // if no authmethods are found on the user, redirect to set one up - if ( - authMethodResponse && - authMethodResponse.authMethodTypes && - authMethodResponse.authMethodTypes.length == 0 - ) { - const params = new URLSearchParams({ - sessionId: session.id, - }); - - if (session.factors?.user?.loginName) { - params.set("loginName", session.factors?.user?.loginName); - } - return { redirect: `/authenticator/set?${params}` }; - } - - const loginSettings = await getLoginSettings({ - serviceUrl, - organization: user.details?.resourceOwner, - }); - - // redirect to mfa factor if user has one, or redirect to set one up - const mfaFactorCheck = await checkMFAFactors( - serviceUrl, - session, - loginSettings, - authMethodResponse.authMethodTypes, - command.organization, - command.requestId, - ); - - if (mfaFactorCheck?.redirect) { - return mfaFactorCheck; - } - - // login user if no additional steps are required - if (command.requestId && session.id) { - const nextUrl = await getNextUrl( - { - sessionId: session.id, - requestId: command.requestId, - organization: - command.organization ?? session.factors?.user?.organizationId, - }, - loginSettings?.defaultRedirectUri, - ); - - return { redirect: nextUrl }; - } - - const url = await getNextUrl( - { - loginName: session.factors.user.loginName, - organization: session.factors?.user?.organizationId, - }, - loginSettings?.defaultRedirectUri, - ); - - return { redirect: url }; } diff --git a/apps/login/src/lib/zitadel.ts b/apps/login/src/lib/zitadel.ts index b7f0f9a059..bdf46a5905 100644 --- a/apps/login/src/lib/zitadel.ts +++ b/apps/login/src/lib/zitadel.ts @@ -502,21 +502,6 @@ export async function verifyInviteCode({ return userService.verifyInviteCode({ userId, verificationCode }, {}); } -export async function resendInviteCode({ - serviceUrl, - userId, -}: { - serviceUrl: string; - userId: string; -}) { - const userService: Client = await createServiceForHost( - UserService, - serviceUrl, - ); - - return userService.resendInviteCode({ userId }, {}); -} - export async function sendEmailCode({ serviceUrl, userId,