Skip to content

Commit

Permalink
fix: optimize error handling waiting for verification and confirmatio…
Browse files Browse the repository at this point in the history
…n expired (#1731)
  • Loading branch information
nac62116 authored Nov 12, 2024
1 parent 403d793 commit 837475e
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 26 deletions.
16 changes: 16 additions & 0 deletions app/routes/auth/verify.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@ import { createProfile, sendWelcomeMail } from "../register/utils.server";
import { updateProfileEmailByUserId } from "./verify.server";
import { invariantResponse } from "~/lib/utils/response";
import * as Sentry from "@sentry/remix";
import { detectLanguage } from "~/root.server";
import i18next from "~/i18next.server";

const i18nNS = ["routes/login"];
export const handle = {
i18n: i18nNS,
};

export async function loader({ request }: LoaderFunctionArgs) {
const locale = detectLanguage(request);
const t = await i18next.getFixedT(locale, i18nNS);
const requestUrl = new URL(request.url);
const token_hash = requestUrl.searchParams.get("token_hash");
const type = requestUrl.searchParams.get("type") as EmailOtpType | null;
Expand All @@ -18,6 +27,13 @@ export async function loader({ request }: LoaderFunctionArgs) {
type,
token_hash,
});
if (
error !== null &&
(error.code === "otp_expired" ||
error.message === "Email link is invalid or has expired")
) {
return redirect(`/login?error=${t("error.confirmationLinkExpired")}`);
}
invariantResponse(
error === null && data.user !== null && data.session !== null,
"Server Error during verification",
Expand Down
58 changes: 48 additions & 10 deletions app/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
getProfileCount,
getProjectCount,
} from "./utils.server";
import { detectLanguage } from "~/root.server";
import i18next from "~/i18next.server";

const i18nNS = ["routes/index", "help"];
export const handle = {
Expand Down Expand Up @@ -80,6 +82,9 @@ const mutation = makeDomainFunction(schema)(async (values) => {
});

export const action = async ({ request }: ActionFunctionArgs) => {
const locale = detectLanguage(request);
const t = await i18next.getFixedT(locale, i18nNS);

const submission = await performMutation({
request,
schema,
Expand All @@ -94,13 +99,30 @@ export const action = async ({ request }: ActionFunctionArgs) => {
);

if (error !== null) {
if (error.message === "Invalid login credentials") {
if (
error.code === "invalid_credentials" ||
error.message === "Invalid login credentials"
) {
return json({
error: {
message: t("login.invalidCredentials"),
},
});
} else if (
error.code === "email_not_confirmed" ||
error.message === "Email not confirmed"
) {
return json({
message:
"Deine Anmeldedaten (E-Mail oder Passwort) sind nicht korrekt. Bitte überprüfe Deine Eingaben.",
error: {
message: t("login.notConfirmed"),
supportMail: process.env.SUPPORT_MAIL,
},
});
} else {
throw json({ message: "Server Error" }, { status: 500 });
throw json(
{ message: `${error.code}: ${error.message}` },
{ status: 500 }
);
}
}
if (submission.data.loginRedirect) {
Expand All @@ -122,9 +144,7 @@ export default function Index() {
const loaderData = useLoaderData<typeof loader>();
const actionData = useActionData<typeof action>();
const loginError =
actionData !== undefined && "message" in actionData
? actionData.message
: null;
actionData !== undefined && "error" in actionData ? actionData.error : null;
const [urlSearchParams] = useSearchParams();
const loginRedirect = urlSearchParams.get("login_redirect");
const handleKeyPress = (event: KeyboardEvent<HTMLFormElement>) => {
Expand Down Expand Up @@ -223,9 +243,27 @@ export default function Index() {
>
{({ Field, Errors, register }) => (
<>
<Errors className="mv-p-3 mv-mb-3 mv-bg-error mv-text-white">
{loginError}
</Errors>
{loginError !== null ? (
<Errors className="mv-p-3 mv-mb-3 mv-bg-negative-100 mv-text-negative-900 mv-rounded-md">
{"supportMail" in loginError ? (
<Trans
i18nKey="error.notConfirmed"
ns={i18nNS}
components={[
<a
key="support-mail"
href={`mailto:${loginError.supportMail}`}
className="mv-text-primary font-bold hover:underline"
>
{" "}
</a>,
]}
/>
) : (
loginError.message
)}
</Errors>
) : null}

<Field name="email" label="E-Mail">
{({ Errors }) => (
Expand Down
76 changes: 66 additions & 10 deletions app/routes/login/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { json, redirect } from "@remix-run/node";
import {
Link,
useActionData,
useLoaderData,
useSearchParams,
useSubmit,
} from "@remix-run/react";
import { makeDomainFunction } from "domain-functions";
import { type TFunction } from "i18next";
import { t, type TFunction } from "i18next";
import type { KeyboardEvent } from "react";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import type { FormProps } from "remix-forms";
import { performMutation } from "remix-forms";
import type { SomeZodObject } from "zod";
Expand Down Expand Up @@ -50,6 +51,17 @@ export const loader = async (args: LoaderFunctionArgs) => {
return redirect("/dashboard");
}

const searchParams = new URL(request.url).searchParams;
if (searchParams.has("error")) {
return json({
error: {
message: t("error.confirmationLinkExpired"),
type: "confirmationLinkExpired",
supportMail: process.env.SUPPORT_MAIL,
},
});
}

return null;
};

Expand All @@ -76,12 +88,31 @@ export const action = async ({ request }: ActionFunctionArgs) => {
);

if (error !== null) {
if (error.message === "Invalid login credentials") {
if (
error.code === "invalid_credentials" ||
error.message === "Invalid login credentials"
) {
return json({
message: t("error.invalidCredentials"),
error: {
message: t("error.invalidCredentials"),
},
});
} else if (
error.code === "email_not_confirmed" ||
error.message === "Email not confirmed"
) {
return json({
error: {
message: t("error.notConfirmed"),
type: "notConfirmed",
supportMail: process.env.SUPPORT_MAIL,
},
});
} else {
throw json({ message: "Server Error" }, { status: 500 });
throw json(
{ message: `${error.code}: ${error.message}` },
{ status: 500 }
);
}
}
if (submission.data.loginRedirect) {
Expand All @@ -100,9 +131,12 @@ export const action = async ({ request }: ActionFunctionArgs) => {

export default function Index() {
const actionData = useActionData<typeof action>();
const loaderData = useLoaderData<typeof loader>();
const loginError =
actionData !== undefined && "message" in actionData
? actionData.message
loaderData !== null
? loaderData.error
: actionData !== undefined && "error" in actionData
? actionData.error
: null;
const [urlSearchParams] = useSearchParams();
const loginRedirect = urlSearchParams.get("login_redirect");
Expand Down Expand Up @@ -145,9 +179,31 @@ export default function Index() {
</div>
<h1 className="mb-8">{t("content.headline")}</h1>

<Errors className="mv-p-3 mv-mb-3 mv-bg-error mv-text-white">
{loginError}
</Errors>
{loginError !== null ? (
<Errors className="mv-p-3 mv-mb-3 mv-bg-negative-100 mv-text-negative-900 mv-rounded-md">
{"supportMail" in loginError && "type" in loginError ? (
<Trans
i18nKey={
loginError.type === "notConfirmed"
? "error.notConfirmed"
: "error.confirmationLinkExpired"
}
ns={i18nNS}
components={[
<a
key="support-mail"
href={`mailto:${loginError.supportMail}`}
className="mv-text-primary font-bold hover:underline"
>
{" "}
</a>,
]}
/>
) : (
loginError.message
)}
</Errors>
) : null}

<div className="mb-4">
<Field name="email" label="E-Mail">
Expand Down
12 changes: 10 additions & 2 deletions app/routes/register/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,16 @@ const createMutation = (t: TFunction) => {
},
loginRedirect
);
if (error !== null && error.message !== "User already registered") {
throw error.message;

if (
error !== null &&
error.code !== "user_already_exists" &&
error.message !== "User already registered"
) {
throw json(
{ message: `${error.code}: ${error.message}` },
{ status: 500 }
);
}

return values;
Expand Down
4 changes: 3 additions & 1 deletion public/locales/de/routes/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"noMember": "Noch kein Mitglied?",
"registerByEmail": "Registrieren mit E-Mail",
"createMintId": "MINT-ID erstellen",
"register": "Registrieren"
"register": "Registrieren",
"invalidCredentials": "Deine Anmeldedaten (E-Mail oder Passwort) sind nicht korrekt. Bitte überprüfe Deine Eingaben.",
"notConfirmed": "Deine E-Mail-Adresse wurde noch nicht bestätigt. Bitte überprüfe Dein Postfach und klicke auf den Bestätigungslink. Wenn Du keine E-Mail erhalten hast, überprüfe bitte Deinen Spam-Ordner oder melde Dich beim <0>Support</0>."
},
"form": {
"label": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/de/routes/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
}
},
"error": {
"invalidCredentials": "Deine Anmeldedaten (E-Mail oder Passwort) sind nicht korrekt. Bitte überprüfe Deine Eingaben."
"invalidCredentials": "Deine Anmeldedaten (E-Mail oder Passwort) sind nicht korrekt. Bitte überprüfe Deine Eingaben.",
"notConfirmed": "Deine E-Mail-Adresse wurde noch nicht bestätigt. Bitte überprüfe Dein Postfach und klicke auf den Bestätigungslink. Wenn Du keine E-Mail erhalten hast, überprüfe bitte Deinen Spam-Ordner oder melde Dich beim <0>Support</0>.",
"confirmationLinkExpired": "Dein Bestätigungslink ist abgelaufen. Bitte melde Dich beim <0>Support</0> um einen neuen anzufordern."
},
"content": {
"headline": "Anmelden",
Expand Down
4 changes: 3 additions & 1 deletion public/locales/en/routes/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"noMember": "Not a member yet?",
"registerByEmail": "Register with email",
"createMintId": "Create MINT-ID",
"register": "Register"
"register": "Register",
"invalidCredentials": "Your login credentials (email or password) are incorrect. Please check your entries.",
"notConfirmed": "Your email address has not yet been confirmed. Please check your inbox and click on the confirmation link. If you haven't received an email, please check your spam folder or contact <0>Support</0>."
},
"form": {
"label": {
Expand Down
4 changes: 3 additions & 1 deletion public/locales/en/routes/login.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
}
},
"error": {
"invalidCredentials": "Your login credentials (email or password) are incorrect. Please check your entries."
"invalidCredentials": "Your login credentials (email or password) are incorrect. Please check your entries.",
"notConfirmed": "Your email address has not yet been confirmed. Please check your inbox and click on the confirmation link. If you haven't received an email, please check your spam folder or contact <0>Support</0>.",
"confirmationLinkExpired": "Your confirmation link has expired. Please contact <0>Support</0> to request a new one."
},
"content": {
"headline": "Login",
Expand Down

0 comments on commit 837475e

Please sign in to comment.