mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 11:27:33 +00:00
Merge branch 'qa' into ldap
This commit is contained in:
@@ -134,7 +134,6 @@ export default async function Page(props: {
|
||||
|
||||
{!(loginName || sessionId) && <Alert>{tError("unknownContext")}</Alert>}
|
||||
|
||||
{/* 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 && <Alert>{tError("sessionExpired")}</Alert>}
|
||||
|
||||
{isSessionValid(sessionWithData).valid &&
|
||||
|
@@ -45,7 +45,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
||||
|
||||
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<any> }) {
|
||||
});
|
||||
} else {
|
||||
await sendEmailCode({
|
||||
serviceUrl,
|
||||
userId,
|
||||
urlTemplate:
|
||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
|
||||
|
@@ -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<string>("");
|
||||
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const router = useRouter();
|
||||
|
||||
async function submitAndContinue(): Promise<boolean | void> {
|
||||
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 (
|
||||
<>
|
||||
<Alert type={AlertType.INFO}>{t("success")}</Alert>
|
||||
|
||||
{error && (
|
||||
<div className="py-4">
|
||||
<Alert>{error}</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mt-8 flex w-full flex-row items-center">
|
||||
<BackButton />
|
||||
<span className="flex-grow"></span>
|
||||
{authMethods?.length === 0 && (
|
||||
<Button
|
||||
onClick={() => submitAndContinue()}
|
||||
type="submit"
|
||||
className="self-end"
|
||||
variant={ButtonVariants.Primary}
|
||||
>
|
||||
{loading && <Spinner className="h-5 w-5 mr-2" />}
|
||||
{t("setupAuthenticator")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@@ -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 };
|
||||
}
|
||||
|
@@ -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<typeof UserService> = await createServiceForHost(
|
||||
UserService,
|
||||
serviceUrl,
|
||||
);
|
||||
|
||||
return userService.resendInviteCode({ userId }, {});
|
||||
}
|
||||
|
||||
export async function sendEmailCode({
|
||||
serviceUrl,
|
||||
userId,
|
||||
|
Reference in New Issue
Block a user