feat: Enable two-factor authentication on login page

This commit is contained in:
Simon Larsen
2024-07-28 19:37:33 -06:00
parent ee3de663b5
commit df8d2f25ee
3 changed files with 70 additions and 45 deletions

View File

@@ -79,20 +79,29 @@ const LoginPage: () => JSX.Element = () => {
src={OneUptimeLogo}
alt="OneUptime"
/>
{!showTwoFactorAuth && <><h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
Sign in to your account
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Join thousands of business that use OneUptime to help them stay online
all the time.
</p></>}
{!showTwoFactorAuth && (
<>
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
Sign in to your account
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Join thousands of business that use OneUptime to help them stay
online all the time.
</p>
</>
)}
{showTwoFactorAuth && <><h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
Two Factor Authentication
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Select two factor authentication method. You will be asked to enter a code from the selected method.
</p></>}
{showTwoFactorAuth && (
<>
<h2 className="mt-6 text-center text-2xl tracking-tight text-gray-900">
Two Factor Authentication
</h2>
<p className="mt-2 text-center text-sm text-gray-600">
Select two factor authentication method. You will be asked to
enter a code from the selected method.
</p>
</>
)}
</div>
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
@@ -143,10 +152,15 @@ const LoginPage: () => JSX.Element = () => {
value: User | JSONObject,
miscData: JSONObject | undefined,
) => {
if ((miscData as JSONObject)["twoFactorAuth"] === true) {
if (
miscData &&
(miscData as JSONObject)["twoFactorAuth"] === true
) {
const twoFactorAuthList: Array<UserTwoFactorAuth> =
UserTwoFactorAuth.fromJSONArray(
(miscData as JSONObject)["twoFactorAuthList"] as JSONArray,
(miscData as JSONObject)[
"twoFactorAuthList"
] as JSONArray,
UserTwoFactorAuth,
);
setTwoFactorAuthList(twoFactorAuthList);
@@ -196,7 +210,7 @@ const LoginPage: () => JSX.Element = () => {
description: "Enter the code from your authenticator app",
required: true,
dataTestId: "code",
fieldType: FormFieldSchemaType.Text
fieldType: FormFieldSchemaType.Text,
},
]}
submitButtonText={"Login"}
@@ -208,7 +222,8 @@ const LoginPage: () => JSX.Element = () => {
try {
const code: string = data["code"] as string;
const twoFactorAuthId: string = selectedTwoFactorAuth.id?.toString() as string;
const twoFactorAuthId: string =
selectedTwoFactorAuth.id?.toString() as string;
const result: HTTPErrorResponse | HTTPResponse<JSONObject> =
await API.post(VERIFY_TWO_FACTOR_AUTH_API_URL, {
@@ -242,24 +257,31 @@ const LoginPage: () => JSX.Element = () => {
)}
</div>
<div className="mt-10 text-center">
{!selectedTwoFactorAuth && <div className="text-muted mb-0 text-gray-500">
Don&apos;t have an account?{" "}
<Link
to={new Route("/accounts/register")}
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
>
Register.
</Link>
</div>}
{selectedTwoFactorAuth ? <div className="text-muted mb-0 text-gray-500">
<Link
onClick={() => {
setSelectedTwoFactorAuth(undefined);
}}
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
>
Select a different two factor authentication method
</Link></div> : <></>}
{!selectedTwoFactorAuth && (
<div className="text-muted mb-0 text-gray-500">
Don&apos;t have an account?{" "}
<Link
to={new Route("/accounts/register")}
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
>
Register.
</Link>
</div>
)}
{selectedTwoFactorAuth ? (
<div className="text-muted mb-0 text-gray-500">
<Link
onClick={() => {
setSelectedTwoFactorAuth(undefined);
}}
className="text-indigo-500 hover:text-indigo-900 cursor-pointer"
>
Select a different two factor authentication method
</Link>
</div>
) : (
<></>
)}
</div>
</div>
</div>

View File

@@ -632,7 +632,9 @@ const login: LoginFunction = async (options: {
return Response.sendErrorResponse(
req,
res,
new BadDataException("Invalid login: Email or password does not match."),
new BadDataException(
"Invalid login: Email or password does not match.",
),
);
}
@@ -654,14 +656,16 @@ const login: LoginFunction = async (options: {
);
}
return Response.sendEntityResponse(
req, res, user, User, {
miscData: {
twoFactorAuthList: UserTwoFactorAuth.toJSONArray(twoFactorAuthList, UserTwoFactorAuth),
twoFactorAuth: true
},
});
}
return Response.sendEntityResponse(req, res, user, User, {
miscData: {
twoFactorAuthList: UserTwoFactorAuth.toJSONArray(
twoFactorAuthList,
UserTwoFactorAuth,
),
twoFactorAuth: true,
},
});
}
if (verifyTwoFactorAuth) {
// code from req

View File

@@ -58,7 +58,6 @@ export class Service extends DatabaseService<Model> {
protected override async onBeforeDelete(
deleteBy: DeleteBy<Model>,
): Promise<OnDelete<Model>> {
const itemsToBeDeleted: Array<Model> = await this.findBy({
query: deleteBy.query,
select: {