From 223c78ac6716009d0802b3058d303ee24530220e Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 25 Jul 2026 19:55:56 -0400 Subject: [PATCH 1/2] Add error pages --- src/App.tsx | 4 ++ src/pages/auth/error.tsx | 124 +++++++++++++++++++++++++++++++++++++++ src/pages/not-found.tsx | 39 ++++++++++++ src/providers/auth.ts | 12 +++- 4 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 src/pages/auth/error.tsx create mode 100644 src/pages/not-found.tsx diff --git a/src/App.tsx b/src/App.tsx index ce2aa83..ccd76fa 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -33,6 +33,8 @@ import Register from "./pages/register"; import ForgotPassword from "./pages/forgot-password"; import ResetPassword from "./pages/reset-password"; import AcceptInvite from "./pages/accept-invite"; +import AuthErrorPage from "./pages/auth/error"; +import NotFoundPage from "./pages/not-found"; import InviteCreate from "./pages/invites/create"; import InvitesList from "./pages/invites/list"; import JoinClass from "./pages/join-class"; @@ -144,6 +146,8 @@ function App() { } /> + } /> + } /> diff --git a/src/pages/auth/error.tsx b/src/pages/auth/error.tsx new file mode 100644 index 0000000..4164451 --- /dev/null +++ b/src/pages/auth/error.tsx @@ -0,0 +1,124 @@ +import { useMemo } from "react"; +import { useSearchParams } from "react-router"; +import { useLink } from "@refinedev/core"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; + +const ERROR_MESSAGES: Record = { + account_not_linked: { + title: "Account not linked", + description: + "This Google or GitHub account uses an email that already has a password login. Sign in with email/password first, or use the same method you used to register.", + }, + unable_to_link_account: { + title: "Could not link account", + description: + "We couldn't connect that social account to your user. Try again, or sign in with email and password.", + }, + oauth_provider_not_found: { + title: "Sign-in provider unavailable", + description: + "That login provider isn't configured. Try another method or contact support.", + }, + invalid_code: { + title: "Sign-in expired", + description: + "The login link expired or was already used. Please try signing in again.", + }, + state_mismatch: { + title: "Sign-in interrupted", + description: + "Something went wrong during the login redirect. Close extra tabs and try again.", + }, + access_denied: { + title: "Access denied", + description: + "You cancelled the sign-in request or didn't grant permission. Try again if that was a mistake.", + }, + email_not_verified: { + title: "Email not verified", + description: + "Your social account email must be verified before you can sign in this way.", + }, +}; + +function resolveError(code: string | null, rawMessage: string | null) { + if (code && ERROR_MESSAGES[code]) { + return { code, ...ERROR_MESSAGES[code] }; + } + + if (rawMessage) { + return { + code: code ?? "unknown", + title: "Sign-in failed", + description: rawMessage, + }; + } + + return { + code: code ?? "unknown", + title: "Something went wrong", + description: + "We couldn't complete sign-in. Please try again or return to the login page.", + }; +} + +export default function AuthErrorPage() { + const [params] = useSearchParams(); + const Link = useLink(); + + const error = useMemo(() => { + const code = + params.get("error") ?? + params.get("errorCode") ?? + params.get("code"); + const message = + params.get("error_description") ?? + params.get("message") ?? + params.get("errorDescription"); + return resolveError(code, message); + }, [params]); + + return ( +
+ + +

+ Error +

+ {error.title} + + {error.description} + +
+ + + + + {error.code !== "unknown" && ( +

+ CODE: {error.code} +

+ )} +
+ + + + + +
+
+ ); +} diff --git a/src/pages/not-found.tsx b/src/pages/not-found.tsx new file mode 100644 index 0000000..763e1df --- /dev/null +++ b/src/pages/not-found.tsx @@ -0,0 +1,39 @@ +import { useLink } from "@refinedev/core"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; + +export default function NotFoundPage() { + const Link = useLink(); + + return ( +
+ + +

+ 404 +

+ Page not found + + That URL doesn't exist in this app. Check the link or go back home. + +
+ + + + + +
+
+ ); +} diff --git a/src/providers/auth.ts b/src/providers/auth.ts index 68b52db..886556b 100644 --- a/src/providers/auth.ts +++ b/src/providers/auth.ts @@ -2,12 +2,17 @@ import type { AuthProvider } from "@refinedev/core"; import { authClient } from "@/lib/auth-client"; import type { Identity } from "@/types"; +/** Absolute frontend URL for OAuth / email redirects (not the API host). */ +const frontendOrigin = + typeof window !== "undefined" ? window.location.origin : ""; + export const authProvider: AuthProvider = { login: async ({ email, password, providerName }) => { if (providerName) { const { error } = await authClient.signIn.social({ provider: providerName, - callbackURL: "/", + callbackURL: `${frontendOrigin}/`, + errorCallbackURL: `${frontendOrigin}/auth/error`, }); if (error) { @@ -36,7 +41,8 @@ export const authProvider: AuthProvider = { if (providerName) { const { error } = await authClient.signIn.social({ provider: providerName, - callbackURL: "/", + callbackURL: `${frontendOrigin}/`, + errorCallbackURL: `${frontendOrigin}/auth/error`, }); if (error) { @@ -107,7 +113,7 @@ export const authProvider: AuthProvider = { forgotPassword: async ({ email }) => { const { error } = await authClient.requestPasswordReset({ email, - redirectTo: "/reset-password", + redirectTo: `${frontendOrigin}/reset-password`, }); if (error) { From ae27b731a9b76375a579f2c28f1f0842402ebd18 Mon Sep 17 00:00:00 2001 From: Vincent Date: Sat, 25 Jul 2026 22:23:29 -0400 Subject: [PATCH 2/2] Address comment --- src/pages/auth/error.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/auth/error.tsx b/src/pages/auth/error.tsx index 4164451..2c436a1 100644 --- a/src/pages/auth/error.tsx +++ b/src/pages/auth/error.tsx @@ -51,7 +51,7 @@ const ERROR_MESSAGES: Record = { }; function resolveError(code: string | null, rawMessage: string | null) { - if (code && ERROR_MESSAGES[code]) { + if (code && Object.prototype.hasOwnProperty.call(ERROR_MESSAGES, code)) { return { code, ...ERROR_MESSAGES[code] }; }