checkafter redirect, continue after setup

This commit is contained in:
peintnermax
2024-04-29 16:42:24 +02:00
parent 9a9ae48cda
commit 88030ff2b9
5 changed files with 104 additions and 24 deletions

View File

@@ -20,7 +20,7 @@ export default async function Page({
}: { }: {
searchParams: Record<string | number | symbol, string | undefined>; searchParams: Record<string | number | symbol, string | undefined>;
}) { }) {
const { loginName, altPassword, authRequestId, organization, sessionId } = const { loginName, checkAfter, authRequestId, organization, sessionId } =
searchParams; searchParams;
const sessionFactors = sessionId const sessionFactors = sessionId
@@ -95,6 +95,7 @@ export default async function Page({
organization={organization} organization={organization}
loginSettings={loginSettings} loginSettings={loginSettings}
userMethods={sessionFactors.authMethods ?? []} userMethods={sessionFactors.authMethods ?? []}
checkAfter={checkAfter === "true"}
></ChooseSecondFactorToSetup> ></ChooseSecondFactorToSetup>
) : ( ) : (
<Alert>No second factors available to setup.</Alert> <Alert>No second factors available to setup.</Alert>

View File

@@ -7,11 +7,14 @@ import {
server, server,
} from "#/lib/zitadel"; } from "#/lib/zitadel";
import Alert from "#/ui/Alert"; import Alert from "#/ui/Alert";
import { Button, ButtonVariants } from "#/ui/Button";
import DynamicTheme from "#/ui/DynamicTheme"; import DynamicTheme from "#/ui/DynamicTheme";
import { Spinner } from "#/ui/Spinner";
import TOTPRegister from "#/ui/TOTPRegister"; import TOTPRegister from "#/ui/TOTPRegister";
import UserAvatar from "#/ui/UserAvatar"; import UserAvatar from "#/ui/UserAvatar";
import { getMostRecentCookieWithLoginname } from "#/utils/cookies"; import { getMostRecentCookieWithLoginname } from "#/utils/cookies";
import { RegisterTOTPResponse } from "@zitadel/server"; import { RegisterTOTPResponse } from "@zitadel/server";
import Link from "next/link";
import { ClientError } from "nice-grpc"; import { ClientError } from "nice-grpc";
export default async function Page({ export default async function Page({
@@ -21,7 +24,8 @@ export default async function Page({
searchParams: Record<string | number | symbol, string | undefined>; searchParams: Record<string | number | symbol, string | undefined>;
params: Record<string | number | symbol, string | undefined>; params: Record<string | number | symbol, string | undefined>;
}) { }) {
const { loginName, organization, sessionId, authRequestId } = searchParams; const { loginName, organization, sessionId, authRequestId, checkAfter } =
searchParams;
const { method } = params; const { method } = params;
const branding = await getBrandingSettings(server, organization); const branding = await getBrandingSettings(server, organization);
@@ -65,6 +69,34 @@ export default async function Page({
}); });
} }
const paramsToContinue = new URLSearchParams({});
let urlToContinue = "/accounts";
if (authRequestId && sessionId) {
if (sessionId) {
paramsToContinue.append("sessionId", sessionId);
}
if (authRequestId) {
paramsToContinue.append("authRequestId", authRequestId);
}
if (organization) {
paramsToContinue.append("organization", organization);
}
urlToContinue = `/login?` + paramsToContinue;
} else if (loginName) {
if (loginName) {
paramsToContinue.append("loginName", loginName);
}
if (authRequestId) {
paramsToContinue.append("authRequestId", authRequestId);
}
if (organization) {
paramsToContinue.append("organization", organization);
}
urlToContinue = `/signedin?` + paramsToContinue;
}
return ( return (
<DynamicTheme branding={branding}> <DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4"> <div className="flex flex-col items-center space-y-4">
@@ -107,17 +139,39 @@ export default async function Page({
sessionId={sessionId} sessionId={sessionId}
authRequestId={authRequestId} authRequestId={authRequestId}
organization={organization} organization={organization}
checkAfter={checkAfter === "true"}
></TOTPRegister> ></TOTPRegister>
</div>{" "} </div>{" "}
</> </>
) : ( ) : (
<p className="ztdl-p"> <>
{method === "email" <p className="ztdl-p">
? "Code via email was successfully added." {method === "email"
: method === "sms" ? "Code via email was successfully added."
? "Code via SMS was successfully added." : method === "sms"
: ""} ? "Code via SMS was successfully added."
</p> : ""}
</p>
<div className="mt-8 flex w-full flex-row items-center">
<span className="flex-grow"></span>
<Link
href={
checkAfter
? `/otp/${method}?` + new URLSearchParams()
: urlToContinue
}
>
<Button
type="submit"
className="self-end"
variant={ButtonVariants.Primary}
>
continue
</Button>
</Link>
</div>
</>
)} )}
</div> </div>
</DynamicTheme> </DynamicTheme>

View File

@@ -17,6 +17,7 @@ type Props = {
organization?: string; organization?: string;
loginSettings: LoginSettings; loginSettings: LoginSettings;
userMethods: AuthenticationMethodType[]; userMethods: AuthenticationMethodType[];
checkAfter: boolean;
}; };
export default function ChooseSecondFactorToSetup({ export default function ChooseSecondFactorToSetup({
@@ -26,6 +27,7 @@ export default function ChooseSecondFactorToSetup({
organization, organization,
loginSettings, loginSettings,
userMethods, userMethods,
checkAfter,
}: Props) { }: Props) {
const cardClasses = (alreadyAdded: boolean) => const cardClasses = (alreadyAdded: boolean) =>
clsx( clsx(
@@ -47,6 +49,9 @@ export default function ChooseSecondFactorToSetup({
if (organization) { if (organization) {
params.append("organization", organization); params.append("organization", organization);
} }
if (checkAfter) {
params.append("checkAfter", "true");
}
const TOTP = (alreadyAdded: boolean) => { const TOTP = (alreadyAdded: boolean) => {
return ( return (

View File

@@ -120,6 +120,7 @@ export default function PasswordForm({
} else if (loginSettings?.forceMfa && !resp.authFactors?.length) { } else if (loginSettings?.forceMfa && !resp.authFactors?.length) {
const params = new URLSearchParams({ const params = new URLSearchParams({
loginName: resp.factors.user.loginName, loginName: resp.factors.user.loginName,
checkAfter: "true", // this defines if the check is directly made after the setup
}); });
if (authRequestId) { if (authRequestId) {

View File

@@ -23,6 +23,7 @@ type Props = {
sessionId?: string; sessionId?: string;
authRequestId?: string; authRequestId?: string;
organization?: string; organization?: string;
checkAfter?: boolean;
}; };
export default function TOTPRegister({ export default function TOTPRegister({
uri, uri,
@@ -31,6 +32,7 @@ export default function TOTPRegister({
sessionId, sessionId,
authRequestId, authRequestId,
organization, organization,
checkAfter,
}: Props) { }: Props) {
const [error, setError] = useState<string>(""); const [error, setError] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false); const [loading, setLoading] = useState<boolean>(false);
@@ -48,22 +50,13 @@ export default function TOTPRegister({
return verifyTOTP(values.code, loginName, organization) return verifyTOTP(values.code, loginName, organization)
.then((response) => { .then((response) => {
setLoading(false); setLoading(false);
if (authRequestId && sessionId) { // if attribute is set, validate MFA after it is setup, otherwise proceed as usual (when mfa is enforced to login)
const params = new URLSearchParams({ if (checkAfter) {
sessionId: sessionId, const params = new URLSearchParams({});
authRequest: authRequestId,
});
if (organization) { if (loginName) {
params.append("organization", organization); params.append("loginName", loginName);
} }
return router.push(`/login?` + params);
} else if (loginName) {
const params = new URLSearchParams({
loginName,
});
if (authRequestId) { if (authRequestId) {
params.append("authRequestId", authRequestId); params.append("authRequestId", authRequestId);
} }
@@ -71,7 +64,33 @@ export default function TOTPRegister({
params.append("organization", organization); params.append("organization", organization);
} }
return router.push(`/signedin?` + params); return router.push(`/otp/time-based?` + params);
} else {
if (authRequestId && sessionId) {
const params = new URLSearchParams({
sessionId: sessionId,
authRequest: authRequestId,
});
if (organization) {
params.append("organization", organization);
}
return router.push(`/login?` + params);
} else if (loginName) {
const params = new URLSearchParams({
loginName,
});
if (authRequestId) {
params.append("authRequestId", authRequestId);
}
if (organization) {
params.append("organization", organization);
}
return router.push(`/signedin?` + params);
}
} }
}) })
.catch((e) => { .catch((e) => {