mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 16:07:32 +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>}
|
{!(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>}
|
{!valid && <Alert>{tError("sessionExpired")}</Alert>}
|
||||||
|
|
||||||
{isSessionValid(sessionWithData).valid &&
|
{isSessionValid(sessionWithData).valid &&
|
||||||
|
@@ -45,7 +45,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
|||||||
|
|
||||||
if (invite === "true") {
|
if (invite === "true") {
|
||||||
await sendInviteEmailCode({
|
await sendInviteEmailCode({
|
||||||
serviceUrl,
|
|
||||||
userId,
|
userId,
|
||||||
urlTemplate:
|
urlTemplate:
|
||||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}&invite=true` +
|
`${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 {
|
} else {
|
||||||
await sendEmailCode({
|
await sendEmailCode({
|
||||||
serviceUrl,
|
|
||||||
userId,
|
userId,
|
||||||
urlTemplate:
|
urlTemplate:
|
||||||
`${host.includes("localhost") ? "http://" : "https://"}${host}${basePath}/verify?code={{.Code}}&userId={{.UserID}}&organization={{.OrgID}}` +
|
`${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,
|
getSession,
|
||||||
getUserByID,
|
getUserByID,
|
||||||
listAuthenticationMethodTypes,
|
listAuthenticationMethodTypes,
|
||||||
resendInviteCode,
|
|
||||||
verifyEmail,
|
verifyEmail,
|
||||||
verifyInviteCode,
|
verifyInviteCode,
|
||||||
verifyTOTPRegistration,
|
verifyTOTPRegistration,
|
||||||
@@ -17,7 +16,6 @@ import crypto from "crypto";
|
|||||||
import { create } from "@zitadel/client";
|
import { create } from "@zitadel/client";
|
||||||
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
import { Session } from "@zitadel/proto/zitadel/session/v2/session_pb";
|
||||||
import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_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 { cookies, headers } from "next/headers";
|
||||||
import { getNextUrl } from "../client";
|
import { getNextUrl } from "../client";
|
||||||
import { getSessionCookieByLoginName } from "../cookies";
|
import { getSessionCookieByLoginName } from "../cookies";
|
||||||
@@ -282,16 +280,19 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
|
|||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||||
|
|
||||||
return command.isInvite
|
return command.isInvite
|
||||||
? resendInviteCode({
|
? createInviteCode({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId: command.userId,
|
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) => {
|
}).catch((error) => {
|
||||||
if (error.code === 9) {
|
if (error.code === 9) {
|
||||||
return { error: "User is already verified!" };
|
return { error: "User is already verified!" };
|
||||||
}
|
}
|
||||||
return { error: "Could not resend invite" };
|
return { error: "Could not resend invite" };
|
||||||
})
|
})
|
||||||
: sendEmailCode({
|
: zitadelSendEmailCode({
|
||||||
userId: command.userId,
|
userId: command.userId,
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
urlTemplate:
|
urlTemplate:
|
||||||
@@ -300,191 +301,29 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
type sendEmailCommand = {
|
type SendEmailCommand = {
|
||||||
serviceUrl: string;
|
|
||||||
userId: string;
|
userId: string;
|
||||||
urlTemplate: string;
|
urlTemplate: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function sendEmailCode(command: sendEmailCommand) {
|
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,
|
|
||||||
) {
|
|
||||||
const _headers = await headers();
|
const _headers = await headers();
|
||||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||||
|
|
||||||
if (!("loginName" in command || "userId" in command)) {
|
return zitadelSendEmailCode({
|
||||||
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({
|
|
||||||
serviceUrl,
|
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 }, {});
|
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({
|
export async function sendEmailCode({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId,
|
userId,
|
||||||
|
Reference in New Issue
Block a user