From 902ca3813310688e2465b1d8c2989b264e386f9f Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 07:24:32 +0000 Subject: [PATCH 1/8] layout --- docs/src/pages/index.tsx | 2 +- src/components/auth/oauthLogin.tsx | 2 +- src/components/auth/registerForm.tsx | 167 ++++++++++++------------- src/components/auth/submitButtons.tsx | 2 +- src/components/auth/welcomeMessage.tsx | 25 +++- src/components/layouts/layout.tsx | 12 +- src/pages/admin/settings/index.tsx | 2 +- src/pages/auth/login/index.tsx | 41 +++--- src/styles/globals.css | 4 + 9 files changed, 141 insertions(+), 116 deletions(-) diff --git a/docs/src/pages/index.tsx b/docs/src/pages/index.tsx index f69e1f14..3536a692 100644 --- a/docs/src/pages/index.tsx +++ b/docs/src/pages/index.tsx @@ -31,7 +31,7 @@ const LandingPage = (): JSX.Element => {

ZTNET Documentation

- Zerotier Controller Web UI + Zerotier Controller Web UI

{ type="button" onClick={oAuthHandler} className={cn( - "btn btn-block btn-primary cursor-pointer rounded-full font-semibold tracking-wide shadow-lg", + "btn btn-block btn-primary cursor-pointer font-semibold tracking-wide shadow-lg", )} > {loading ? : null} diff --git a/src/components/auth/registerForm.tsx b/src/components/auth/registerForm.tsx index 530dec14..ebc902c9 100644 --- a/src/components/auth/registerForm.tsx +++ b/src/components/auth/registerForm.tsx @@ -60,97 +60,94 @@ const RegisterForm: React.FC = () => { }); }; return ( -
-
-
-

Register

-

Please sign up with your credentials

-
-
- {invite && ( - - )} +
+
+

Sign up with credentials

+
+ + {invite && ( - - - } + placeholder="Invitation code" /> - - - - - } - /> - - - - } - /> -
- -
- -
- Copyright © {new Date().getFullYear()} Kodea Solutions + + + + } + /> + + + + } + /> +
+
+ +
+ Copyright © {new Date().getFullYear()} Kodea Solutions
); diff --git a/src/components/auth/submitButtons.tsx b/src/components/auth/submitButtons.tsx index a04435db..c9329f4a 100644 --- a/src/components/auth/submitButtons.tsx +++ b/src/components/auth/submitButtons.tsx @@ -9,7 +9,7 @@ const SubmitButtons: React.FC = ({ loading, isTotp }) => ( -
- -
- Copyright © {new Date().getFullYear()} Kodea Solutions + + + } + /> +
+
-
+ ); }; diff --git a/src/components/auth/welcomeMessage.tsx b/src/components/auth/welcomeMessage.tsx index 2b2e7f91..93ba860b 100644 --- a/src/components/auth/welcomeMessage.tsx +++ b/src/components/auth/welcomeMessage.tsx @@ -22,7 +22,12 @@ export const WelcomeMessage = () => { ZTNET
- + https://ztnet.network
diff --git a/src/components/layouts/header.tsx b/src/components/layouts/header.tsx index 0619e080..7b720b83 100644 --- a/src/components/layouts/header.tsx +++ b/src/components/layouts/header.tsx @@ -41,7 +41,7 @@ const Header = forwardRef>( title="ztnet logo" src={ZtnetLogo.src} /> - + {globalSiteTitle} diff --git a/src/components/layouts/layout.tsx b/src/components/layouts/layout.tsx index a5b90ebb..cf2ef247 100644 --- a/src/components/layouts/layout.tsx +++ b/src/components/layouts/layout.tsx @@ -25,11 +25,6 @@ interface Props { } export const LayoutPublic = ({ children }: Props): JSX.Element => { - const router = useRouter(); - const { data: options, isLoading: loadingRegistration } = - api.public.registrationAllowed.useQuery(); - - const currentPath = router.pathname; return (
{/*
diff --git a/src/pages/auth/login/index.tsx b/src/pages/auth/login/index.tsx index b1869a62..ef919142 100644 --- a/src/pages/auth/login/index.tsx +++ b/src/pages/auth/login/index.tsx @@ -8,10 +8,16 @@ import { LayoutPublic } from "~/components/layouts/layout"; import OauthLogin from "~/components/auth/oauthLogin"; import CredentialsForm from "~/components/auth/credentialsForm"; import Link from "next/link"; +import { useRouter } from "next/router"; +import { api } from "~/utils/api"; const Login = ({ title, oauthExclusiveLogin, hasOauth }) => { const currentYear = new Date().getFullYear(); + const router = useRouter(); + const { data: options, isLoading: loadingRegistration } = + api.public.registrationAllowed.useQuery(); + const currentPath = router.pathname; return ( <> @@ -33,13 +39,14 @@ const Login = ({ title, oauthExclusiveLogin, hasOauth }) => {
)} - -
-

Don't have an account?

- - Get Started! - -
+ {options?.enableRegistration && !loadingRegistration ? ( +
+

Don't have an account?

+ + Get Started! + +
+ ) : null}
Copyright © {currentYear} Kodea Solutions diff --git a/src/pages/auth/register/index.tsx b/src/pages/auth/register/index.tsx index 76043ec5..aff18ad3 100644 --- a/src/pages/auth/register/index.tsx +++ b/src/pages/auth/register/index.tsx @@ -9,6 +9,7 @@ import { prisma } from "~/server/db"; import { globalSiteTitle } from "~/utils/global"; import { useRouter } from "next/router"; import RegisterOrganizationInviteForm from "~/components/auth/registerOrganizationInvite"; +import Link from "next/link"; const Register = () => { const title = `${globalSiteTitle} - Sign Up`; @@ -23,11 +24,23 @@ const Register = () => { - {organizationInvite ? ( - - ) : ( - - )} +
+

Sign up with credentials

+ {organizationInvite ? ( + + ) : ( + + )} +
+

Have an account?

+ + Login! + +
+
+ Copyright © {new Date().getFullYear()} Kodea Solutions +
+
); }; diff --git a/src/server/api/routers/authRouter.ts b/src/server/api/routers/authRouter.ts index 47aaaa1a..46709eea 100644 --- a/src/server/api/routers/authRouter.ts +++ b/src/server/api/routers/authRouter.ts @@ -70,7 +70,7 @@ export const authRouter = createTRPCRouter({ .email() .transform((val) => val.trim()), password: passwordSchema("password does not meet the requirements!"), - name: z.string().min(3).max(40), + name: z.string().min(3, "Name must contain at least 3 character(s)").max(40), expiresAt: z.string().optional(), ztnetInvitationCode: z.string().optional(), ztnetOrganizationToken: z.string().optional(), From 7725c46d79268b473c1f337e689b90e6b69f73b2 Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 11:22:40 +0000 Subject: [PATCH 3/8] public pages layout --- src/components/auth/credentialsForm.tsx | 7 +- src/components/auth/forgotPasswordForm.tsx | 62 ++++++-------- ...submitButtons.tsx => formSubmitButton.tsx} | 8 +- src/components/auth/mfaRecoveryForm.tsx | 65 ++++++-------- src/components/auth/registerForm.tsx | 12 +-- .../auth/registerOrganizationInvite.tsx | 13 +-- src/components/layouts/layout.tsx | 6 +- src/pages/auth/forgotPassword/index.tsx | 21 ++++- src/pages/auth/forgotPassword/reset/index.tsx | 85 +++++++++++-------- src/pages/auth/login/index.tsx | 4 - src/pages/auth/mfaRecovery/index.tsx | 21 ++++- src/pages/auth/mfaRecovery/reset/index.tsx | 15 ++-- 12 files changed, 164 insertions(+), 155 deletions(-) rename src/components/auth/{submitButtons.tsx => formSubmitButton.tsx} (65%) diff --git a/src/components/auth/credentialsForm.tsx b/src/components/auth/credentialsForm.tsx index e4050e37..e16b8dbb 100644 --- a/src/components/auth/credentialsForm.tsx +++ b/src/components/auth/credentialsForm.tsx @@ -5,7 +5,7 @@ import { toast } from "react-hot-toast"; import { ErrorCode } from "~/utils/errorCode"; import Link from "next/link"; import TOTPInput from "./totpInput"; -import SubmitButtons from "./submitButtons"; +import FormSubmitButtons from "./formSubmitButton"; import FormInput from "./formInput"; interface FormData { @@ -122,7 +122,10 @@ const CredentialsForm: React.FC = () => { )} {showOTP && }
- +
); diff --git a/src/components/auth/forgotPasswordForm.tsx b/src/components/auth/forgotPasswordForm.tsx index 90cc51a3..5be6721a 100644 --- a/src/components/auth/forgotPasswordForm.tsx +++ b/src/components/auth/forgotPasswordForm.tsx @@ -1,8 +1,9 @@ import { useState } from "react"; import { api } from "~/utils/api"; -import cn from "classnames"; import { toast } from "react-hot-toast"; import { useTrpcApiErrorHandler } from "~/hooks/useTrpcApiHandler"; +import FormInput from "./formInput"; +import FormSubmitButtons from "./formSubmitButton"; interface FormData { email: string; @@ -44,43 +45,30 @@ const ForgotPasswordForm: React.FC = () => { }); }; return ( -
-
-
-

Forgot Password

-

- We will send you a reset link if the email exist -

-
-
-
- - -
-
- -
-
-
- Copyright © {new Date().getFullYear()} Kodea Solutions -
+
+ + + + + } + /> +
+
-
+ ); }; diff --git a/src/components/auth/submitButtons.tsx b/src/components/auth/formSubmitButton.tsx similarity index 65% rename from src/components/auth/submitButtons.tsx rename to src/components/auth/formSubmitButton.tsx index c9329f4a..f1b53034 100644 --- a/src/components/auth/submitButtons.tsx +++ b/src/components/auth/formSubmitButton.tsx @@ -2,10 +2,10 @@ import cn from "classnames"; interface SubmitButtonProps { loading: boolean; - isTotp: boolean; + title: string; } -const SubmitButtons: React.FC = ({ loading, isTotp }) => ( +const FormSubmitButtons: React.FC = ({ loading, title }) => ( ); -export default SubmitButtons; +export default FormSubmitButtons; diff --git a/src/components/auth/mfaRecoveryForm.tsx b/src/components/auth/mfaRecoveryForm.tsx index 89ce3089..39df5575 100644 --- a/src/components/auth/mfaRecoveryForm.tsx +++ b/src/components/auth/mfaRecoveryForm.tsx @@ -1,8 +1,9 @@ import { useState } from "react"; import { api } from "~/utils/api"; -import cn from "classnames"; import { toast } from "react-hot-toast"; import { useTrpcApiErrorHandler } from "~/hooks/useTrpcApiHandler"; +import FormInput from "./formInput"; +import FormSubmitButtons from "./formSubmitButton"; interface FormData { email: string; @@ -33,7 +34,7 @@ const MfaRecoveryForm: React.FC = () => { onSuccess: () => { setLoading(false); setFormData({ email: "" }); - toast.success("Password reset link sent to your email if the account exist!", { + toast.success("2FA reset procedure sent to your email if the account exist!", { duration: 10000, }); }, @@ -44,43 +45,31 @@ const MfaRecoveryForm: React.FC = () => { }); }; return ( -
-
-
-

2FA Recovery

-

- We will send you instructions on how to recover your account -

-
-
-
- - -
-
- -
-
-
- Copyright © {new Date().getFullYear()} Kodea Solutions -
+
+ + + + + } + /> + +
+
-
+ ); }; diff --git a/src/components/auth/registerForm.tsx b/src/components/auth/registerForm.tsx index a1f59fa5..50bb9729 100644 --- a/src/components/auth/registerForm.tsx +++ b/src/components/auth/registerForm.tsx @@ -2,9 +2,9 @@ import { signIn } from "next-auth/react"; import { useRouter } from "next/router"; import { useState } from "react"; import { api } from "~/utils/api"; -import cn from "classnames"; import { useTrpcApiErrorHandler } from "~/hooks/useTrpcApiHandler"; import FormInput from "./formInput"; +import FormSubmitButtons from "./formSubmitButton"; interface FormData { email: string; password: string; @@ -131,15 +131,7 @@ const RegisterForm: React.FC = () => { } />
- +
); diff --git a/src/components/auth/registerOrganizationInvite.tsx b/src/components/auth/registerOrganizationInvite.tsx index 3c013247..ee870840 100644 --- a/src/components/auth/registerOrganizationInvite.tsx +++ b/src/components/auth/registerOrganizationInvite.tsx @@ -1,11 +1,10 @@ -// import { signIn } from "next-auth/react"; import { signIn } from "next-auth/react"; import { useRouter } from "next/router"; import { useState } from "react"; import { api } from "~/utils/api"; -import cn from "classnames"; import { toast } from "react-hot-toast"; import { Organization, Role, User } from "@prisma/client"; +import FormSubmitButtons from "./formSubmitButton"; interface FormData { email: string; @@ -189,15 +188,7 @@ const RegisterOrganizationInviteForm: React.FC = ({ />
- +
diff --git a/src/components/layouts/layout.tsx b/src/components/layouts/layout.tsx index cf2ef247..f89a4988 100644 --- a/src/components/layouts/layout.tsx +++ b/src/components/layouts/layout.tsx @@ -47,10 +47,10 @@ export const LayoutPublic = ({ children }: Props): JSX.Element => { ) : null}
*/} -
+
{/* Main section */} -
-
+
+
{children}
diff --git a/src/pages/auth/forgotPassword/index.tsx b/src/pages/auth/forgotPassword/index.tsx index b609c556..b7da2311 100644 --- a/src/pages/auth/forgotPassword/index.tsx +++ b/src/pages/auth/forgotPassword/index.tsx @@ -2,6 +2,7 @@ import { GetServerSideProps, GetServerSidePropsContext } from "next"; import { Session } from "next-auth"; import { getSession } from "next-auth/react"; import Head from "next/head"; +import Link from "next/link"; import React, { ReactElement } from "react"; import ForgotPasswordForm from "~/components/auth/forgotPasswordForm"; import { LayoutPublic } from "~/components/layouts/layout"; @@ -16,7 +17,25 @@ const ForgotPassword = () => { - + +
+

Forgot Password

+
+

+ We will send you a reset link if the account exist +

+
+ +
+ + Back to Login + +
+
+ Copyright © {new Date().getFullYear()} Kodea Solutions +
+
+
); }; diff --git a/src/pages/auth/forgotPassword/reset/index.tsx b/src/pages/auth/forgotPassword/reset/index.tsx index 3ed4caf0..748c12e0 100644 --- a/src/pages/auth/forgotPassword/reset/index.tsx +++ b/src/pages/auth/forgotPassword/reset/index.tsx @@ -5,13 +5,16 @@ import { toast } from "react-hot-toast"; import { type ErrorData, type ZodErrorFieldErrors } from "~/types/errorHandling"; import Head from "next/head"; import { globalSiteTitle } from "~/utils/global"; +import FormInput from "~/components/auth/formInput"; +import FormSubmitButtons from "~/components/auth/formSubmitButton"; const ForgotPassword = () => { const router = useRouter(); const { token } = router.query; const [state, setState] = useState({ password: "", newPassword: "" }); - const { mutate: resetPassword } = api.auth.changePasswordFromJwt.useMutation(); + const { mutate: resetPassword, isLoading } = + api.auth.changePasswordFromJwt.useMutation(); const { data: tokenData, isLoading: validateTokenLoading } = api.auth.validateResetPasswordToken.useQuery( { @@ -29,7 +32,7 @@ const ForgotPassword = () => { }, ); - const handleSubmit = (e: React.MouseEvent) => { + const submitHandler = (e: React.FormEvent) => { e.preventDefault(); resetPassword( { @@ -85,39 +88,53 @@ const ForgotPassword = () => {

Reset Password

Please enter your new password

-
-
- - -
-
- - -
+ + + + + } + /> + + + + } + />
- +
diff --git a/src/pages/auth/login/index.tsx b/src/pages/auth/login/index.tsx index ef919142..3be659e2 100644 --- a/src/pages/auth/login/index.tsx +++ b/src/pages/auth/login/index.tsx @@ -3,21 +3,17 @@ import Head from "next/head"; import { type Session } from "next-auth"; import { getSession } from "next-auth/react"; import { ReactElement } from "react"; -import { globalSiteTitle } from "~/utils/global"; import { LayoutPublic } from "~/components/layouts/layout"; import OauthLogin from "~/components/auth/oauthLogin"; import CredentialsForm from "~/components/auth/credentialsForm"; import Link from "next/link"; -import { useRouter } from "next/router"; import { api } from "~/utils/api"; const Login = ({ title, oauthExclusiveLogin, hasOauth }) => { const currentYear = new Date().getFullYear(); - const router = useRouter(); const { data: options, isLoading: loadingRegistration } = api.public.registrationAllowed.useQuery(); - const currentPath = router.pathname; return ( <> diff --git a/src/pages/auth/mfaRecovery/index.tsx b/src/pages/auth/mfaRecovery/index.tsx index ded93d3d..dae54b5a 100644 --- a/src/pages/auth/mfaRecovery/index.tsx +++ b/src/pages/auth/mfaRecovery/index.tsx @@ -2,6 +2,7 @@ import { GetServerSideProps, GetServerSidePropsContext } from "next"; import { Session } from "next-auth"; import { getSession } from "next-auth/react"; import Head from "next/head"; +import Link from "next/link"; import React, { ReactElement } from "react"; import MfaRecoveryForm from "~/components/auth/mfaRecoveryForm"; import { LayoutPublic } from "~/components/layouts/layout"; @@ -16,7 +17,25 @@ const MfaRecovery = () => { - +
+
+
+

2FA Recovery

+

+ We will send you instructions on how to recover your account +

+
+ +
+ + Back to Login + +
+
+ Copyright © {new Date().getFullYear()} Kodea Solutions +
+
+
); }; diff --git a/src/pages/auth/mfaRecovery/reset/index.tsx b/src/pages/auth/mfaRecovery/reset/index.tsx index 1c56747f..1a96e265 100644 --- a/src/pages/auth/mfaRecovery/reset/index.tsx +++ b/src/pages/auth/mfaRecovery/reset/index.tsx @@ -6,12 +6,13 @@ import { type ErrorData, type ZodErrorFieldErrors } from "~/types/errorHandling" import Head from "next/head"; import { globalSiteTitle } from "~/utils/global"; import FormInput from "~/components/auth/formInput"; +import FormSubmitButtons from "~/components/auth/formSubmitButton"; const MfaRecoveryReset = () => { const router = useRouter(); const { token } = router.query; const [state, setState] = useState({ email: "", password: "", recoveryCode: "" }); - const { mutate: resetMfa } = api.mfaAuth.mfaResetValidation.useMutation(); + const { mutate: resetMfa, isLoading } = api.mfaAuth.mfaResetValidation.useMutation(); const { data: tokenData, isLoading: validateTokenLoading } = api.mfaAuth.mfaValidateToken.useQuery( @@ -30,7 +31,7 @@ const MfaRecoveryReset = () => { }, ); - const handleSubmit = (e: React.MouseEvent) => { + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); resetMfa( { @@ -88,7 +89,7 @@ const MfaRecoveryReset = () => { Please enter your credentials and Recovery Code

-
+ { } />
- +
From ea85bc696b4900eea0db7970ed4177b9569a79ab Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 11:51:24 +0000 Subject: [PATCH 4/8] layout --- src/components/auth/welcomeMessage.tsx | 73 ++++++++++++++------------ src/components/layouts/layout.tsx | 20 ------- src/styles/globals.css | 4 ++ 3 files changed, 44 insertions(+), 53 deletions(-) diff --git a/src/components/auth/welcomeMessage.tsx b/src/components/auth/welcomeMessage.tsx index 93ba860b..27b2fb54 100644 --- a/src/components/auth/welcomeMessage.tsx +++ b/src/components/auth/welcomeMessage.tsx @@ -3,45 +3,52 @@ import ZtnetLogo from "docs/images/logo/ztnet_200x178.png"; import Link from "next/link"; export const WelcomeMessage = () => { - const { data: options } = api.public.getWelcomeMessage.useQuery(); + const { data: options, isLoading } = api.public.getWelcomeMessage.useQuery(); return (
-
- {options?.welcomeMessageTitle || ( -
-

-
- ztnet logo - ZTNET + {!isLoading && ( +
+
+ {options?.welcomeMessageTitle ? ( +

{options.welcomeMessageTitle}

+ ) : ( +
+

+
+ ztnet logo + ZTNET +
+

+ + https://ztnet.network +
-

- - https://ztnet.network - + )}
- )} -
-

- {options?.welcomeMessageBody || ( - - ZeroTier VPN is your key to boundless connectivity and ultimate privacy. - Experience a secure and borderless digital world, free from limitations. - Empower yourself with unmatched performance, while safeguarding your data. - - )} -

+

+ {options?.welcomeMessageBody || ( + + ZeroTier VPN is your key to boundless connectivity and ultimate privacy. + Experience a secure and borderless digital world, free from limitations. + Empower yourself with unmatched performance, while safeguarding your + data. + + )} +

+
+ )}
); diff --git a/src/components/layouts/layout.tsx b/src/components/layouts/layout.tsx index f89a4988..e7f374c9 100644 --- a/src/components/layouts/layout.tsx +++ b/src/components/layouts/layout.tsx @@ -27,26 +27,6 @@ interface Props { export const LayoutPublic = ({ children }: Props): JSX.Element => { return (
- {/*
-
-

- {globalSiteTitle} -

-
- -
- {options?.enableRegistration && !loadingRegistration ? ( - - {currentPath === "/auth/register" ? "Login" : "Sign Up"} - - ) : null} -
-
*/}
{/* Main section */}
diff --git a/src/styles/globals.css b/src/styles/globals.css index 14de41a8..116d9364 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -82,6 +82,10 @@ color: rgb(255, 180, 65); } +.fade-in { + animation: fadeIn 0.5s ease-in; +} + /* There is a bug issue over at Mozilla that is still open, given that it was created 5 years ago. https://bugzilla.mozilla.org/show_bug.cgi?id=1576820 From 6f8fe5a8b552fef3272135f734173297c470cabd Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 13:36:57 +0000 Subject: [PATCH 5/8] tests --- src/__tests__/pages/auth/signin.test.tsx | 8 +++++++- src/components/layouts/layout.tsx | 3 --- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/__tests__/pages/auth/signin.test.tsx b/src/__tests__/pages/auth/signin.test.tsx index db045723..5aa95bd1 100644 --- a/src/__tests__/pages/auth/signin.test.tsx +++ b/src/__tests__/pages/auth/signin.test.tsx @@ -30,6 +30,12 @@ jest.mock("../../../utils/api", () => ({ getWelcomeMessage: { useQuery: jest.fn(), }, + registrationAllowed: { + useQuery: jest.fn().mockReturnValue({ + data: { allowed: true }, + isLoading: false, + }), + }, }, network: { getUserNetworks: { @@ -63,7 +69,7 @@ describe("LoginPage", () => { render( - + , ); diff --git a/src/components/layouts/layout.tsx b/src/components/layouts/layout.tsx index e7f374c9..8045fcd0 100644 --- a/src/components/layouts/layout.tsx +++ b/src/components/layouts/layout.tsx @@ -3,11 +3,8 @@ import FourOhFour from "~/pages/404"; import Header from "./header"; import Sidebar from "./sidebar"; import Footer from "./footer"; -import { globalSiteTitle } from "~/utils/global"; -import Link from "next/link"; import { useRouter } from "next/router"; import { User } from "@prisma/client"; -import { api } from "~/utils/api"; import { useSidebarStore, useAsideChatStore, useFontSizeStore } from "~/utils/store"; import ChatAside from "./chatAside"; import { LogsFooter } from "./logFooter"; From 48307329f4244ee76c0645f9f230f50de0afdcaf Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 14:15:14 +0000 Subject: [PATCH 6/8] error handling --- src/pages/auth/mfaRecovery/reset/index.tsx | 19 ++++++++++++---- src/server/api/routers/mfaAuthRouter.ts | 26 +++++++++++++--------- src/utils/errorCode.ts | 1 + 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/pages/auth/mfaRecovery/reset/index.tsx b/src/pages/auth/mfaRecovery/reset/index.tsx index 1a96e265..a17c5be4 100644 --- a/src/pages/auth/mfaRecovery/reset/index.tsx +++ b/src/pages/auth/mfaRecovery/reset/index.tsx @@ -7,6 +7,7 @@ import Head from "next/head"; import { globalSiteTitle } from "~/utils/global"; import FormInput from "~/components/auth/formInput"; import FormSubmitButtons from "~/components/auth/formSubmitButton"; +import { ErrorCode } from "~/utils/errorCode"; const MfaRecoveryReset = () => { const router = useRouter(); @@ -20,13 +21,23 @@ const MfaRecoveryReset = () => { token: token as string, }, { + enabled: !!token, onSuccess: (response) => { if (response?.error) { - router.push("/auth/login"); + switch (response.error) { + case ErrorCode.InvalidToken: + void router.push("/auth/login"); + break; + case ErrorCode.TooManyRequests: + toast.error("Too many requests, please try again later"); + break; + default: + toast.error(response.error); + } } }, - onError: () => { - router.push("/auth/login"); + onError: (error) => { + toast.error(error.message); }, }, ); @@ -71,7 +82,7 @@ const MfaRecoveryReset = () => { }; const title = `${globalSiteTitle} - Reset MFA`; - if (validateTokenLoading || !tokenData || tokenData.error) { + if (validateTokenLoading || !tokenData) { return null; } return ( diff --git a/src/server/api/routers/mfaAuthRouter.ts b/src/server/api/routers/mfaAuthRouter.ts index f8803953..fef1b8ef 100644 --- a/src/server/api/routers/mfaAuthRouter.ts +++ b/src/server/api/routers/mfaAuthRouter.ts @@ -45,19 +45,23 @@ export const mfaAuthRouter = createTRPCRouter({ const { token } = input; if (!token) return { error: ErrorCode.InvalidToken }; + let decoded: { id: string; email: string }; + try { const secret = generateInstanceSecret(TOTP_MFA_TOKEN_SECRET); - const decoded = jwt.verify(token, secret) as { id: string; email: string }; + decoded = jwt.verify(token, secret) as { id: string; email: string }; + } catch { + return { error: ErrorCode.InvalidToken }; + } - // add rate limit - try { - await limiter.check(ctx.res, GENERAL_REQUEST_LIMIT, "MFA_RESET_LINK"); - } catch { - throw new TRPCError({ - code: "TOO_MANY_REQUESTS", - message: "Rate limit exceeded", - }); - } + // add rate limit + try { + await limiter.check(ctx.res, GENERAL_REQUEST_LIMIT, "MFA_RESET_LINK"); + } catch { + return { error: ErrorCode.TooManyRequests }; + } + + try { const user = await ctx.prisma.user.findFirst({ where: { id: decoded.id, @@ -72,7 +76,7 @@ export const mfaAuthRouter = createTRPCRouter({ return { email: user.email }; } catch (_error) { - return { error: ErrorCode.InvalidToken }; + return { error: _error.message }; } }), mfaResetLink: publicProcedure diff --git a/src/utils/errorCode.ts b/src/utils/errorCode.ts index db37d471..0348d266 100644 --- a/src/utils/errorCode.ts +++ b/src/utils/errorCode.ts @@ -14,4 +14,5 @@ export enum ErrorCode { InternalServerError = "internal-server-error", NewPasswordMatchesOld = "new-password-matches-old", ThirdPartyIdentityProviderEnabled = "third-party-identity-provider-enabled", + TooManyRequests = "too-many-requests", } From f5093c9891941521baa6b450c9363bef83bb5c36 Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Wed, 7 Aug 2024 14:31:00 +0000 Subject: [PATCH 7/8] limit --- src/server/api/routers/mfaAuthRouter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/api/routers/mfaAuthRouter.ts b/src/server/api/routers/mfaAuthRouter.ts index fef1b8ef..f0f6f93c 100644 --- a/src/server/api/routers/mfaAuthRouter.ts +++ b/src/server/api/routers/mfaAuthRouter.ts @@ -20,7 +20,7 @@ const limiter = rateLimit({ uniqueTokenPerInterval: 1000, }); -const GENERAL_REQUEST_LIMIT = 5; +const GENERAL_REQUEST_LIMIT = 10; async function verifyRecoveryCode( providedCode: string, From d30c8b7c8ee2cb47cfc9a3599973a7e75b2c4712 Mon Sep 17 00:00:00 2001 From: Bernt Christian Egeland Date: Thu, 8 Aug 2024 09:01:21 +0000 Subject: [PATCH 8/8] layout --- src/components/auth/formInput.tsx | 3 + .../auth/registerOrganizationInvite.tsx | 151 +++++++++++------- 2 files changed, 92 insertions(+), 62 deletions(-) diff --git a/src/components/auth/formInput.tsx b/src/components/auth/formInput.tsx index d7e26216..c8c50427 100644 --- a/src/components/auth/formInput.tsx +++ b/src/components/auth/formInput.tsx @@ -7,6 +7,7 @@ interface FormInputProps { value: string; onChange: (event: React.ChangeEvent) => void; placeholder: string; + disabled?: boolean; icon?: ReactElement; } @@ -17,6 +18,7 @@ const FormInput: React.FC = ({ value, onChange, placeholder, + disabled = false, icon, }) => { return ( @@ -28,6 +30,7 @@ const FormInput: React.FC = ({ {icon && React.cloneElement(icon, { className: "h-4 w-4 opacity-70" })} = ({ ); } return ( -
-
-
-

Organization Invitation

-

Please sign up with your credentials

-
-
- {organizationInvite ? ( -
- - + {organizationInvite ? ( + + -
- ) : null} -
- - -
-
- - -
-
- - + } + /> + ) : null} + + + + } + /> + + + + + } + /> + + -
-
- -
-
-
- Copyright © {new Date().getFullYear()} Kodea Solutions -
+ + } + /> +
+
-
+ ); };