fix: handle auth callback failures
This commit is contained in:
@@ -26,17 +26,18 @@ type Mode = "login" | "signup";
|
||||
|
||||
type LoginClientProps = {
|
||||
nextPath: string;
|
||||
initialError?: string;
|
||||
initialMode?: Mode;
|
||||
};
|
||||
|
||||
export function LoginClient({ nextPath, initialMode }: LoginClientProps) {
|
||||
export function LoginClient({ nextPath, initialError, initialMode }: LoginClientProps) {
|
||||
const router = useRouter();
|
||||
const { locale } = useI18n();
|
||||
const [mode, setMode] = useState<Mode>(initialMode ?? "login");
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [errorText, setErrorText] = useState("");
|
||||
const [errorText, setErrorText] = useState(initialError || "");
|
||||
const [infoText, setInfoText] = useState("");
|
||||
const [resetSent, setResetSent] = useState(false);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
@@ -121,6 +122,10 @@ export function LoginClient({ nextPath, initialMode }: LoginClientProps) {
|
||||
/>
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setErrorText(initialError || "");
|
||||
}, [initialError]);
|
||||
|
||||
const onResetPassword = async () => {
|
||||
setErrorText("");
|
||||
setInfoText("");
|
||||
|
||||
@@ -33,8 +33,16 @@ export function runTests() {
|
||||
"callback",
|
||||
"route.ts",
|
||||
);
|
||||
const loginPagePath = path.join(
|
||||
projectRoot,
|
||||
"app",
|
||||
"auth",
|
||||
"login",
|
||||
"page.tsx",
|
||||
);
|
||||
|
||||
const loginClientSource = fs.readFileSync(loginClientPath, "utf8");
|
||||
const loginPageSource = fs.readFileSync(loginPagePath, "utf8");
|
||||
|
||||
assert(
|
||||
loginClientSource.includes("/auth/reset-password") &&
|
||||
@@ -80,4 +88,22 @@ export function runTests() {
|
||||
authCallbackSource.includes("Bearer"),
|
||||
"auth callback must warm /api/auth/me with the exchanged Supabase session so new users receive the signup trial immediately",
|
||||
);
|
||||
assert(
|
||||
loginPageSource.includes("error?: string") &&
|
||||
loginPageSource.includes("normalizeAuthError") &&
|
||||
loginPageSource.includes("params.error") &&
|
||||
loginPageSource.includes("initialError={initialError}") &&
|
||||
loginClientSource.includes("initialError?: string") &&
|
||||
loginClientSource.includes("useState(initialError || \"\")"),
|
||||
"login page must surface auth callback errors in the login form instead of silently returning users to the terminal",
|
||||
);
|
||||
assert(
|
||||
authCallbackSource.includes("redirectToLoginWithError") &&
|
||||
authCallbackSource.includes('request.nextUrl.searchParams.get("error_description")') &&
|
||||
authCallbackSource.includes('request.nextUrl.searchParams.get("error")') &&
|
||||
authCallbackSource.includes("exchangeError") &&
|
||||
authCallbackSource.includes("exchangeCodeForSession(code)") &&
|
||||
authCallbackSource.includes("auth_error"),
|
||||
"auth callback must redirect failed OAuth/session exchanges back to login with an error message",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,12 @@ export function runTests() {
|
||||
authMeRouteSource.includes("total"),
|
||||
"/api/auth/me proxy must expose stage durations through Server-Timing for HAR inspection",
|
||||
);
|
||||
assert(
|
||||
authMeRouteSource.includes('response.headers.set("Cache-Control", "no-store")') &&
|
||||
authMeRouteSource.indexOf('response.headers.set("Cache-Control", "no-store")') >
|
||||
authMeRouteSource.indexOf("function finishAuthMeResponse"),
|
||||
"/api/auth/me proxy must mark every auth profile response no-store so anonymous state cannot be reused after login",
|
||||
);
|
||||
const finishStart = authMeRouteSource.indexOf("function finishAuthMeResponse");
|
||||
const finishEnd = authMeRouteSource.indexOf("async function trackAuthDiagnosticEvent");
|
||||
const finishSource =
|
||||
|
||||
Reference in New Issue
Block a user