combined otp form

This commit is contained in:
peintnermax
2024-04-17 14:14:25 +02:00
parent 57db64f6bb
commit 20a589cea2
5 changed files with 154 additions and 57 deletions

View File

@@ -1,35 +1,44 @@
"use client";
import { useEffect, useState } from "react";
import { Button, ButtonVariants } from "./Button";
import { TextInput } from "./Input";
import { useForm } from "react-hook-form";
import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { Spinner } from "./Spinner";
import { coerceToArrayBuffer, coerceToBase64Url } from "#/utils/base64";
import { Button, ButtonVariants } from "./Button";
import Alert from "./Alert";
import { Spinner } from "./Spinner";
import { Checks } from "@zitadel/server";
import { useForm } from "react-hook-form";
import { TextInput } from "./Input";
// either loginName or sessionId must be provided
type Props = {
loginName?: string;
sessionId?: string;
authRequestId?: string;
organization?: string;
method?: string;
code?: string;
};
type Inputs = {
code: string;
};
type Props = {
loginName: string | undefined;
sessionId: string | undefined;
code: string | undefined;
method: string;
authRequestId?: string;
organization?: string;
submit: boolean;
};
export default function TOTPForm({
export default function LoginOTP({
loginName,
code,
method,
sessionId,
authRequestId,
organization,
submit,
method,
code,
}: Props) {
const [error, setError] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false);
const router = useRouter();
const initialized = useRef(false);
const { register, handleSubmit, formState } = useForm<Inputs>({
mode: "onBlur",
defaultValues: {
@@ -37,10 +46,93 @@ export default function TOTPForm({
},
});
const router = useRouter();
useEffect(() => {
if (!initialized.current) {
initialized.current = true;
setLoading(true);
updateSessionForOTPChallenge();
// .then((response) => {
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string>("");
// setLoading(false);
// })
// .catch((error) => {
// setError(error);
// setLoading(false);
// });
}
}, []);
async function updateSessionForOTPChallenge() {
setLoading(true);
const res = await fetch("/api/session", {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
loginName,
sessionId,
organization,
challenges:
method === "email"
? {
otpEmail: true,
}
: method === "sms"
? { otpSms: true }
: {},
authRequestId,
}),
});
setLoading(false);
if (!res.ok) {
const error = await res.json();
throw error.details.details;
}
return res.json();
}
// async function submitLogin(inputs: Inputs) {
// setLoading(true);
// const checks: Checks = {};
// if (method === "email") {
// checks.otpEmail = {
// code: inputs.code,
// };
// }
// if (method === "sms") {
// checks.otpSms = {
// code: inputs.code,
// };
// }
// const res = await fetch("/api/session", {
// method: "PUT",
// headers: {
// "Content-Type": "application/json",
// },
// body: JSON.stringify({
// loginName,
// sessionId,
// organization,
// authRequestId,
// checks,
// }),
// });
// const response = await res.json();
// setLoading(false);
// if (!res.ok) {
// setError(response.details);
// return Promise.reject(response.details);
// }
// return response;
// }
async function submitCode(values: Inputs, organization?: string) {
setLoading(true);
@@ -58,12 +150,29 @@ export default function TOTPForm({
body.authRequestId = authRequestId;
}
const res = await fetch("/api/otp/verify", {
const checks: Checks = {};
if (method === "sms") {
checks.otpSms = { code: values.code };
}
if (method === "email") {
checks.otpEmail = { code: values.code };
}
if (method === "time-based") {
checks.totp = { code: values.code };
}
const res = await fetch("/api/session", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
body: JSON.stringify({
loginName,
sessionId,
organization,
checks,
authRequestId,
}),
});
setLoading(false);
@@ -112,13 +221,6 @@ export default function TOTPForm({
const { errors } = formState;
useEffect(() => {
if (submit && code) {
// When we navigate to this page, we always want to be redirected if submit is true and the parameters are valid.
setCodeAndContinue({ code }, organization);
}
}, []);
return (
<form className="w-full">
<div className="">

View File

@@ -6,6 +6,7 @@ import { coerceToArrayBuffer, coerceToBase64Url } from "#/utils/base64";
import { Button, ButtonVariants } from "./Button";
import Alert from "./Alert";
import { Spinner } from "./Spinner";
import { Checks } from "@zitadel/server";
// either loginName or sessionId must be provided
type Props = {
@@ -100,7 +101,9 @@ export default function LoginPasskey({
loginName,
sessionId,
organization,
webAuthN: { credentialAssertionData: data },
checks: {
webAuthN: { credentialAssertionData: data },
} as Checks,
authRequestId,
}),
});

View File

@@ -7,7 +7,7 @@ import { useForm } from "react-hook-form";
import { useRouter } from "next/navigation";
import { Spinner } from "./Spinner";
import Alert from "./Alert";
import { LoginSettings, AuthFactor } from "@zitadel/server";
import { LoginSettings, AuthFactor, Checks } from "@zitadel/server";
type Inputs = {
password: string;
@@ -52,7 +52,9 @@ export default function PasswordForm({
body: JSON.stringify({
loginName,
organization,
password: values.password,
checks: {
password: { password: values.password },
} as Checks,
authRequestId,
}),
});