mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-13 10:17:33 +00:00
fix: use invite code whenever authmethods are zero, otherwise use email code
This commit is contained in:
@@ -120,6 +120,7 @@ export default async function Page(props: {
|
|||||||
if (!isUserVerified) {
|
if (!isUserVerified) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
loginName: sessionWithData.factors.user.loginName as string,
|
loginName: sessionWithData.factors.user.loginName as string,
|
||||||
|
invite: "true",
|
||||||
send: "true", // set this to true to request a new code immediately
|
send: "true", // set this to true to request a new code immediately
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -134,6 +134,7 @@ 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 &&
|
||||||
|
@@ -2,7 +2,7 @@ import { Alert, AlertType } from "@/components/alert";
|
|||||||
import { DynamicTheme } from "@/components/dynamic-theme";
|
import { DynamicTheme } from "@/components/dynamic-theme";
|
||||||
import { UserAvatar } from "@/components/user-avatar";
|
import { UserAvatar } from "@/components/user-avatar";
|
||||||
import { VerifyForm } from "@/components/verify-form";
|
import { VerifyForm } from "@/components/verify-form";
|
||||||
import { sendEmailCode } from "@/lib/server/verify";
|
import { sendEmailCode, sendInviteEmailCode } from "@/lib/server/verify";
|
||||||
import { getServiceUrlFromHeaders } from "@/lib/service-url";
|
import { getServiceUrlFromHeaders } from "@/lib/service-url";
|
||||||
import { loadMostRecentSession } from "@/lib/session";
|
import { loadMostRecentSession } from "@/lib/session";
|
||||||
import { getBrandingSettings, getUserByID } from "@/lib/zitadel";
|
import { getBrandingSettings, getUserByID } from "@/lib/zitadel";
|
||||||
@@ -21,11 +21,6 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
|||||||
|
|
||||||
const _headers = await headers();
|
const _headers = await headers();
|
||||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||||
const host = _headers.get("host");
|
|
||||||
|
|
||||||
if (!host || typeof host !== "string") {
|
|
||||||
throw new Error("No host found");
|
|
||||||
}
|
|
||||||
|
|
||||||
const branding = await getBrandingSettings({
|
const branding = await getBrandingSettings({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
@@ -41,29 +36,25 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
|||||||
|
|
||||||
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
||||||
|
|
||||||
if ("loginName" in searchParams) {
|
async function sendEmail() {
|
||||||
sessionFactors = await loadMostRecentSession({
|
const host = _headers.get("host");
|
||||||
serviceUrl,
|
|
||||||
sessionParams: {
|
|
||||||
loginName,
|
|
||||||
organization,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (doSend && sessionFactors?.factors?.user?.id) {
|
if (!host || typeof host !== "string") {
|
||||||
await sendEmailCode({
|
throw new Error("No host found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invite === "true") {
|
||||||
|
await sendInviteEmailCode({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId: sessionFactors?.factors?.user?.id,
|
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}}&invite=true` +
|
||||||
(requestId ? `&requestId=${requestId}` : ""),
|
(requestId ? `&requestId=${requestId}` : ""),
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.error("Could not resend verification email", error);
|
console.error("Could not resend verification email", error);
|
||||||
throw Error("Failed to send verification email");
|
throw Error("Failed to send verification email");
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
} else if ("userId" in searchParams && userId) {
|
|
||||||
if (doSend) {
|
|
||||||
await sendEmailCode({
|
await sendEmailCode({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId,
|
userId,
|
||||||
@@ -75,6 +66,24 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
|||||||
throw Error("Failed to send verification email");
|
throw Error("Failed to send verification email");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("loginName" in searchParams) {
|
||||||
|
sessionFactors = await loadMostRecentSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessionParams: {
|
||||||
|
loginName,
|
||||||
|
organization,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (doSend && sessionFactors?.factors?.user?.id) {
|
||||||
|
await sendEmail();
|
||||||
|
}
|
||||||
|
} else if ("userId" in searchParams && userId) {
|
||||||
|
if (doSend) {
|
||||||
|
await sendEmail();
|
||||||
|
}
|
||||||
|
|
||||||
const userResponse = await getUserByID({
|
const userResponse = await getUserByID({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
@@ -151,15 +160,19 @@ export default async function Page(props: { searchParams: Promise<any> }) {
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* always show the code form / TODO improve UI for email links which were already used (currently we get an error code 3 due being reused) */}
|
{/* always show the code form, except code is an invite code and the email is verified */}
|
||||||
<VerifyForm
|
{invite === "true" && human?.email?.isVerified ? (
|
||||||
loginName={loginName}
|
<Alert type={AlertType.INFO}>{t("success")}</Alert>
|
||||||
organization={organization}
|
) : (
|
||||||
userId={id}
|
<VerifyForm
|
||||||
code={code}
|
loginName={loginName}
|
||||||
isInvite={invite === "true"}
|
organization={organization}
|
||||||
requestId={requestId}
|
userId={id}
|
||||||
/>
|
code={code}
|
||||||
|
isInvite={invite === "true"}
|
||||||
|
requestId={requestId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</DynamicTheme>
|
</DynamicTheme>
|
||||||
);
|
);
|
||||||
|
111
apps/login/src/app/(login)/verify/success/page.tsx
Normal file
111
apps/login/src/app/(login)/verify/success/page.tsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { DynamicTheme } from "@/components/dynamic-theme";
|
||||||
|
import { UserAvatar } from "@/components/user-avatar";
|
||||||
|
import { getSessionCookieById } from "@/lib/cookies";
|
||||||
|
import { getServiceUrlFromHeaders } from "@/lib/service-url";
|
||||||
|
import { loadMostRecentSession } from "@/lib/session";
|
||||||
|
import {
|
||||||
|
getBrandingSettings,
|
||||||
|
getLoginSettings,
|
||||||
|
getSession,
|
||||||
|
getUserByID,
|
||||||
|
} from "@/lib/zitadel";
|
||||||
|
import { HumanUser, User } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
||||||
|
import { getLocale, getTranslations } from "next-intl/server";
|
||||||
|
import { headers } from "next/headers";
|
||||||
|
|
||||||
|
async function loadSessionById(
|
||||||
|
serviceUrl: string,
|
||||||
|
sessionId: string,
|
||||||
|
organization?: string,
|
||||||
|
) {
|
||||||
|
const recent = await getSessionCookieById({ sessionId, organization });
|
||||||
|
return getSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessionId: recent.id,
|
||||||
|
sessionToken: recent.token,
|
||||||
|
}).then((response) => {
|
||||||
|
if (response?.session) {
|
||||||
|
return response.session;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Page(props: { searchParams: Promise<any> }) {
|
||||||
|
const searchParams = await props.searchParams;
|
||||||
|
const locale = getLocale();
|
||||||
|
const t = await getTranslations({ locale, namespace: "signedin" });
|
||||||
|
|
||||||
|
const _headers = await headers();
|
||||||
|
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||||
|
|
||||||
|
const { loginName, requestId, organization, userId } = searchParams;
|
||||||
|
|
||||||
|
const branding = await getBrandingSettings({
|
||||||
|
serviceUrl,
|
||||||
|
organization,
|
||||||
|
});
|
||||||
|
|
||||||
|
const sessionFactors = await loadMostRecentSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessionParams: { loginName, organization },
|
||||||
|
}).catch((error) => {
|
||||||
|
console.warn("Error loading session:", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
let loginSettings;
|
||||||
|
if (!requestId) {
|
||||||
|
loginSettings = await getLoginSettings({
|
||||||
|
serviceUrl,
|
||||||
|
organization,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = userId ?? sessionFactors?.factors?.user?.id;
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
throw Error("Failed to get user id");
|
||||||
|
}
|
||||||
|
|
||||||
|
const userResponse = await getUserByID({
|
||||||
|
serviceUrl,
|
||||||
|
userId: id,
|
||||||
|
});
|
||||||
|
|
||||||
|
let user: User | undefined;
|
||||||
|
let human: HumanUser | undefined;
|
||||||
|
|
||||||
|
if (userResponse) {
|
||||||
|
user = userResponse.user;
|
||||||
|
if (user?.type.case === "human") {
|
||||||
|
human = user.type.value as HumanUser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DynamicTheme branding={branding}>
|
||||||
|
<div className="flex flex-col items-center space-y-4">
|
||||||
|
<h1>
|
||||||
|
{t("title", { user: sessionFactors?.factors?.user?.displayName })}
|
||||||
|
</h1>
|
||||||
|
<p className="ztdl-p mb-6 block">{t("description")}</p>
|
||||||
|
|
||||||
|
{sessionFactors ? (
|
||||||
|
<UserAvatar
|
||||||
|
loginName={loginName ?? sessionFactors.factors?.user?.loginName}
|
||||||
|
displayName={sessionFactors.factors?.user?.displayName}
|
||||||
|
showDropdown
|
||||||
|
searchParams={searchParams}
|
||||||
|
></UserAvatar>
|
||||||
|
) : (
|
||||||
|
user && (
|
||||||
|
<UserAvatar
|
||||||
|
loginName={user.preferredLoginName}
|
||||||
|
displayName={human?.profile?.displayName}
|
||||||
|
showDropdown={false}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DynamicTheme>
|
||||||
|
);
|
||||||
|
}
|
@@ -63,6 +63,11 @@ export function VerifyForm({
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response && "error" in response && response?.error) {
|
||||||
|
setError(response.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,6 @@ import { idpTypeToIdentityProviderType, idpTypeToSlug } from "../idp";
|
|||||||
import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
import { PasskeysType } from "@zitadel/proto/zitadel/settings/v2/login_settings_pb";
|
||||||
import { UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
import { UserState } from "@zitadel/proto/zitadel/user/v2/user_pb";
|
||||||
import { getServiceUrlFromHeaders } from "../service-url";
|
import { getServiceUrlFromHeaders } from "../service-url";
|
||||||
import { checkEmailVerified, checkUserVerification } from "../verify-helper";
|
|
||||||
import {
|
import {
|
||||||
getActiveIdentityProviders,
|
getActiveIdentityProviders,
|
||||||
getIDPByID,
|
getIDPByID,
|
||||||
@@ -254,62 +253,63 @@ export async function sendLoginname(command: SendLoginnameCommand) {
|
|||||||
userId: session.factors?.user?.id,
|
userId: session.factors?.user?.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
// this can be expected to be an invite as users created in console have a password set.
|
// always resend invite if user has no auth method set
|
||||||
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
if (!methods.authMethodTypes || !methods.authMethodTypes.length) {
|
||||||
// redirect to /verify invite if no auth method is set and email is not verified
|
// redirect to /verify invite if no auth method is set and email is not verified
|
||||||
const inviteCheck = checkEmailVerified(
|
// const inviteCheck = checkEmailVerified(
|
||||||
session,
|
// session,
|
||||||
humanUser,
|
// humanUser,
|
||||||
session.factors.user.organizationId,
|
// session.factors.user.organizationId,
|
||||||
command.requestId,
|
// command.requestId,
|
||||||
);
|
// );
|
||||||
|
|
||||||
if (inviteCheck?.redirect) {
|
// if (inviteCheck?.redirect) {
|
||||||
return inviteCheck;
|
// return inviteCheck;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// check if user was verified recently
|
// // check if user was verified recently
|
||||||
const isUserVerified = await checkUserVerification(
|
// const isUserVerified = await checkUserVerification(
|
||||||
session.factors.user.id,
|
// session.factors.user.id,
|
||||||
);
|
// );
|
||||||
if (!isUserVerified) {
|
// if (!isUserVerified) {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
loginName: session.factors?.user?.loginName as string,
|
loginName: session.factors?.user?.loginName as string,
|
||||||
send: "true", // set this to true to request a new code immediately
|
send: "true", // set this to true to request a new code immediately
|
||||||
});
|
invite: "true",
|
||||||
|
|
||||||
if (command.requestId) {
|
|
||||||
params.append("requestId", command.requestId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.organization || session.factors?.user?.organizationId) {
|
|
||||||
params.append(
|
|
||||||
"organization",
|
|
||||||
command.organization ??
|
|
||||||
(session.factors?.user?.organizationId as string),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { redirect: `/verify?` + params };
|
|
||||||
}
|
|
||||||
|
|
||||||
const paramsAuthenticatorSetup = new URLSearchParams({
|
|
||||||
loginName: session.factors?.user?.loginName,
|
|
||||||
userId: session.factors?.user?.id, // verify needs user id
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (command.requestId) {
|
||||||
|
params.append("requestId", command.requestId);
|
||||||
|
}
|
||||||
|
|
||||||
if (command.organization || session.factors?.user?.organizationId) {
|
if (command.organization || session.factors?.user?.organizationId) {
|
||||||
paramsAuthenticatorSetup.append(
|
params.append(
|
||||||
"organization",
|
"organization",
|
||||||
command.organization ?? session.factors?.user?.organizationId,
|
command.organization ??
|
||||||
|
(session.factors?.user?.organizationId as string),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.requestId) {
|
return { redirect: `/verify?` + params };
|
||||||
paramsAuthenticatorSetup.append("requestId", command.requestId);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
return { redirect: "/authenticator/set?" + paramsAuthenticatorSetup };
|
// const paramsAuthenticatorSetup = new URLSearchParams({
|
||||||
|
// loginName: session.factors?.user?.loginName,
|
||||||
|
// userId: session.factors?.user?.id, // verify needs user id
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (command.organization || session.factors?.user?.organizationId) {
|
||||||
|
// paramsAuthenticatorSetup.append(
|
||||||
|
// "organization",
|
||||||
|
// command.organization ?? session.factors?.user?.organizationId,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (command.requestId) {
|
||||||
|
// paramsAuthenticatorSetup.append("requestId", command.requestId);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return { redirect: "/authenticator/set?" + paramsAuthenticatorSetup };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (methods.authMethodTypes.length == 1) {
|
if (methods.authMethodTypes.length == 1) {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
createInviteCode,
|
||||||
getLoginSettings,
|
getLoginSettings,
|
||||||
getSession,
|
getSession,
|
||||||
getUserByID,
|
getUserByID,
|
||||||
@@ -93,88 +94,26 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let session: Session | undefined;
|
let session: Session | undefined;
|
||||||
let user: User | undefined;
|
const userResponse = await getUserByID({
|
||||||
|
serviceUrl,
|
||||||
|
userId: command.userId,
|
||||||
|
});
|
||||||
|
|
||||||
if ("loginName" in command) {
|
if (!userResponse || !userResponse.user) {
|
||||||
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 {
|
|
||||||
const userResponse = await getUserByID({
|
|
||||||
serviceUrl,
|
|
||||||
userId: command.userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!userResponse || !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" };
|
return { error: "Could not load user" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginSettings = await getLoginSettings({
|
const user = userResponse.user;
|
||||||
serviceUrl,
|
|
||||||
organization: user.details?.resourceOwner,
|
const sessionCookie = await getSessionCookieByLoginName({
|
||||||
|
loginName:
|
||||||
|
"loginName" in command ? command.loginName : user.preferredLoginName,
|
||||||
|
organization: command.organization,
|
||||||
|
}).catch((error) => {
|
||||||
|
console.warn("Ignored error:", error); // checked later
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// load auth methods for user
|
||||||
const authMethodResponse = await listAuthenticationMethodTypes({
|
const authMethodResponse = await listAuthenticationMethodTypes({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId: user.userId,
|
userId: user.userId,
|
||||||
@@ -190,6 +129,36 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
|
|||||||
authMethodResponse.authMethodTypes &&
|
authMethodResponse.authMethodTypes &&
|
||||||
authMethodResponse.authMethodTypes.length == 0
|
authMethodResponse.authMethodTypes.length == 0
|
||||||
) {
|
) {
|
||||||
|
if (!sessionCookie) {
|
||||||
|
const checks = create(ChecksSchema, {
|
||||||
|
user: {
|
||||||
|
search: {
|
||||||
|
case: "loginName",
|
||||||
|
value: userResponse.user.preferredLoginName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
session = await createSessionAndUpdateCookie({
|
||||||
|
checks,
|
||||||
|
requestId: command.requestId,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
session = await getSession({
|
||||||
|
serviceUrl,
|
||||||
|
sessionId: sessionCookie.id,
|
||||||
|
sessionToken: sessionCookie.token,
|
||||||
|
}).then((response) => {
|
||||||
|
if (response?.session) {
|
||||||
|
return response.session;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return { error: "Could not create session" };
|
||||||
|
}
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
sessionId: session.id,
|
sessionId: session.id,
|
||||||
});
|
});
|
||||||
@@ -218,44 +187,80 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
|
|||||||
return { redirect: `/authenticator/set?${params}` };
|
return { redirect: `/authenticator/set?${params}` };
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirect to mfa factor if user has one, or redirect to set one up
|
// if no session found and user is not invited, only show success page,
|
||||||
const mfaFactorCheck = await checkMFAFactors(
|
// if user is invited, recreate invite flow to not depend on session
|
||||||
serviceUrl,
|
|
||||||
session,
|
|
||||||
loginSettings,
|
|
||||||
authMethodResponse.authMethodTypes,
|
|
||||||
command.organization,
|
|
||||||
command.requestId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (mfaFactorCheck?.redirect) {
|
if (!sessionCookie || !session?.factors?.user?.id) {
|
||||||
return mfaFactorCheck;
|
const verifySuccessParams = new URLSearchParams({});
|
||||||
}
|
|
||||||
|
|
||||||
// login user if no additional steps are required
|
if (command.userId) {
|
||||||
if (command.requestId && session.id) {
|
verifySuccessParams.set("userId", command.userId);
|
||||||
const nextUrl = await getNextUrl(
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
("loginName" in command && command.loginName) ||
|
||||||
|
user.preferredLoginName
|
||||||
|
) {
|
||||||
|
verifySuccessParams.set(
|
||||||
|
"loginName",
|
||||||
|
"loginName" in command && command.loginName
|
||||||
|
? command.loginName
|
||||||
|
: user.preferredLoginName,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (command.requestId) {
|
||||||
|
verifySuccessParams.set("requestId", command.requestId);
|
||||||
|
}
|
||||||
|
if (command.organization) {
|
||||||
|
verifySuccessParams.set("organization", command.organization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { redirect: `/verify/success?${verifySuccessParams}` };
|
||||||
|
} else {
|
||||||
|
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(
|
||||||
{
|
{
|
||||||
sessionId: session.id,
|
loginName: session.factors.user.loginName,
|
||||||
requestId: command.requestId,
|
organization: session.factors?.user?.organizationId,
|
||||||
organization:
|
|
||||||
command.organization ?? session.factors?.user?.organizationId,
|
|
||||||
},
|
},
|
||||||
loginSettings?.defaultRedirectUri,
|
loginSettings?.defaultRedirectUri,
|
||||||
);
|
);
|
||||||
|
|
||||||
return { redirect: nextUrl };
|
return { redirect: url };
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = await getNextUrl(
|
|
||||||
{
|
|
||||||
loginName: session.factors.user.loginName,
|
|
||||||
organization: session.factors?.user?.organizationId,
|
|
||||||
},
|
|
||||||
loginSettings?.defaultRedirectUri,
|
|
||||||
);
|
|
||||||
|
|
||||||
return { redirect: url };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type resendVerifyEmailCommand = {
|
type resendVerifyEmailCommand = {
|
||||||
@@ -279,6 +284,11 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
|
|||||||
? resendInviteCode({
|
? resendInviteCode({
|
||||||
serviceUrl,
|
serviceUrl,
|
||||||
userId: command.userId,
|
userId: command.userId,
|
||||||
|
}).catch((error) => {
|
||||||
|
if (error.code === 9) {
|
||||||
|
return { error: "User is already verified!" };
|
||||||
|
}
|
||||||
|
return { error: "Could not resend invite" };
|
||||||
})
|
})
|
||||||
: sendEmailCode({
|
: sendEmailCode({
|
||||||
userId: command.userId,
|
userId: command.userId,
|
||||||
@@ -303,6 +313,15 @@ export async function sendEmailCode(command: sendEmailCommand) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 = {
|
export type SendVerificationRedirectWithoutCheckCommand = {
|
||||||
organization?: string;
|
organization?: string;
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
|
Reference in New Issue
Block a user