fix(login): one-button Google sign-in + middleware auto-issues dev cookie
Deploy to route.crispygoat.com / deploy (push) Failing after 3s

The dev login flow was redirecting back to /login because:
  - src/middleware.ts didn't exist, so the Auth.js authorized
    callback in auth.config.ts never ran
  - Even if it had, it only checked the Auth.js JWT, not dev_session
  - Clicking the demo buttons set the cookie via document.cookie,
    but the admin layout (via getAdminUser) was the only thing
    reading it — no edge gate

Fix:
  - New src/middleware.ts: gates /admin/* and /login at the edge.
    Auto-issues dev_session=platform_admin when ALLOW_DEV_LOGIN is
    enabled (default on, set to 'false' in prod). No buttons, no
    client-side cookie games.
  - LoginClient.tsx: stripped to a single Google OAuth button.
    Removed email/password form, dev credentials form, and the
    /login?demo=1 three-button picker.
  - Removed signInWithDev from auth-signin.ts (no longer called).
  - Removed dead /dev-login page and /api/dev-login route.

Net result: one sign-in path (Google), invisible dev auto-login
via middleware, no more three modes.
This commit is contained in:
2026-06-06 20:14:08 +00:00
parent 7489da3da0
commit e499139c74
5 changed files with 260 additions and 502 deletions
+3 -27
View File
@@ -1,7 +1,6 @@
"use server";
import { signIn, signOut } from "@/lib/auth";
import { AuthError } from "next-auth";
/**
* Server actions that wrap the Auth.js v5 `signIn` / `signOut` API for
@@ -19,38 +18,15 @@ import { AuthError } from "next-auth";
* <button type="submit">Sign in with Google</button>
* </form>
*
* Usage for the dev credentials provider (dev only):
* <form action={signInWithDev}>
* <input name="username" />
* <input name="password" type="password" />
* <button type="submit">Dev login</button>
* </form>
* Note: dev/demo authentication is no longer a button on the login page.
* `src/middleware.ts` auto-issues the `dev_session` cookie for /admin/*
* when ALLOW_DEV_LOGIN is enabled. See CLAUDE.md.
*/
export async function signInWithGoogle(): Promise<void> {
await signIn("google", { redirectTo: "/admin" });
}
export async function signInWithDev(formData: FormData): Promise<void> {
const username = String(formData.get("username") ?? "admin");
const password = String(formData.get("password") ?? "dev");
try {
await signIn("dev-login", {
username,
password,
redirectTo: "/admin",
});
} catch (e) {
// signIn() throws a `NEXT_REDIRECT` to navigate — let that through
// so the redirect actually happens. Re-throw any other error so the
// caller can render a meaningful message.
if (e instanceof AuthError) {
throw new Error(`Dev sign-in failed: ${e.type}`);
}
throw e;
}
}
export async function signOutAction(): Promise<void> {
await signOut({ redirectTo: "/login" });
}