mfa selection, setup page

This commit is contained in:
peintnermax
2024-04-12 16:38:38 +02:00
parent 57e4585120
commit 437ba4375f
7 changed files with 89 additions and 43 deletions

View File

@@ -1,35 +0,0 @@
"use client";
import { Button, ButtonVariants } from "#/ui/Button";
import { TextInput } from "#/ui/Input";
import UserAvatar from "#/ui/UserAvatar";
import { useRouter } from "next/navigation";
export default function Page() {
const router = useRouter();
return (
<div className="flex flex-col items-center space-y-4">
<h1>Password</h1>
<p className="ztdl-p mb-6 block">Enter your password.</p>
<UserAvatar
showDropdown
displayName="Max Peintner"
loginName="max@zitadel.com"
></UserAvatar>
<div className="w-full">
<TextInput type="password" label="Password" />
</div>
<div className="flex w-full flex-row items-center justify-between">
<Button
onClick={() => router.back()}
variant={ButtonVariants.Secondary}
>
back
</Button>
<Button variant={ButtonVariants.Primary}>continue</Button>
</div>
</div>
);
}

View File

@@ -9,8 +9,8 @@ export default function Page() {
return (
<div className="flex flex-col items-center space-y-4">
<h1>Password</h1>
<p className="ztdl-p mb-6 block">Enter your password.</p>
<h1>Second Factor</h1>
<p className="ztdl-p mb-6 block">Please select a second factor.</p>
<UserAvatar
showDropdown

View File

@@ -0,0 +1,37 @@
import { getBrandingSettings, getLoginSettings, server } from "#/lib/zitadel";
import DynamicTheme from "#/ui/DynamicTheme";
import TOTPForm from "#/ui/TOTPForm";
export default async function Page({
searchParams,
}: {
searchParams: Record<string | number | symbol, string | undefined>;
}) {
const { loginName, authRequestId, sessionId, organization, code, submit } =
searchParams;
const branding = await getBrandingSettings(server, organization);
const loginSettings = await getLoginSettings(server, organization);
return (
<DynamicTheme branding={branding}>
<div className="flex flex-col items-center space-y-4">
<h1>Verify 2-Factor</h1>
<p className="ztdl-p">Enter the code from your authenticator app. </p>
<div>
{loginSettings?.secondFactors.map((factor) => {
return (
<div>
{factor === 1 && <div>TOTP</div>}
{factor === 2 && <div>U2F</div>}
{factor === 3 && <div>OTP Email</div>}
{factor === 4 && <div>OTP Sms</div>}
</div>
);
})}
</div>
</div>
</DynamicTheme>
);
}

View File

@@ -1,4 +1,9 @@
import { server, deleteSession } from "#/lib/zitadel";
import {
server,
deleteSession,
getUserById,
listHumanAuthFactors,
} from "#/lib/zitadel";
import {
SessionCookie,
getMostRecentSessionCookie,
@@ -100,12 +105,23 @@ export async function PUT(request: NextRequest) {
challenges,
undefined,
authRequestId
).then((session) => {
console.log(session);
).then(async (session) => {
// if password, check if user has MFA methods
let authFactors;
if (password && session.factors?.user?.id) {
const response = await listHumanAuthFactors(
server,
session.factors?.user?.id
);
if (response.result && response.result.length) {
authFactors = response.result;
}
}
return NextResponse.json({
sessionId: session.id,
factors: session.factors,
challenges: session.challenges,
authFactors,
});
});
})

View File

@@ -1,4 +1,6 @@
import {
LegalAndSupportSettings,
PasswordComplexitySettings,
ZitadelServer,
ZitadelServerOptions,
user,
@@ -15,8 +17,6 @@ import {
AddHumanUserResponse,
BrandingSettings,
ListSessionsResponse,
LegalAndSupportSettings,
PasswordComplexitySettings,
GetSessionResponse,
VerifyEmailResponse,
Checks,
@@ -40,6 +40,7 @@ import {
CreateCallbackResponse,
RequestChallenges,
TextQueryMethod,
ListHumanAuthFactorsResponse,
AddHumanUserRequest,
} from "@zitadel/server";
@@ -257,6 +258,14 @@ export async function addHumanUser(
);
}
export async function listHumanAuthFactors(
server: ZitadelServer,
userId: string
): Promise<ListHumanAuthFactorsResponse> {
const managementService = management.getManagement(server);
return managementService.listHumanAuthFactors({ userId }, {});
}
export async function listUsers(
userName: string,
organizationId: string

View File

@@ -89,7 +89,7 @@ export default function PasswordForm({
let continueWithMfa = undefined;
if (
loginSettings?.forceMfa &&
loginSettings.secondFactors?.length >= 1 // TODO replace with user methods - if forceMFA is set and no user methods prompt to add method (/mfa/add)
resp.authFactors?.length >= 1 // TODO if forceMFA is set and no user methods prompt to add method (/mfa/add)
) {
if (loginSettings.secondFactors?.length === 1) {
continueWithMfa = loginSettings.secondFactors[0];
@@ -97,6 +97,23 @@ export default function PasswordForm({
// continueWithMfa = loginSettings.secondFactors[0];
// render selection page for mfa (/mfa/select)
}
} else if (loginSettings?.forceMfa && resp.authFactors?.length === 0) {
const params = new URLSearchParams(
authRequestId
? {
loginName: resp.factors.user.loginName,
authRequestId,
}
: {
loginName: resp.factors.user.loginName,
}
);
if (organization) {
params.append("organization", organization);
}
return router.push(`/mfa/set?` + params);
}
// OIDC flows
if (authRequestId && resp && resp.sessionId) {