fix(auth): replace client-side signin page with server-side route handler
The client page rendered inside AppShell layout, causing a flash of the full app UI before redirecting to Authentik. The new route handler initiates the OAuth flow server-side (CSRF token + POST to NextAuth provider signin) and redirects instantly — no visible page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { signIn } from "next-auth/react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* Custom sign-in page that auto-redirects to Authentik.
|
||||
* Skips the default NextAuth provider chooser (no "Sign in with Authentik" button).
|
||||
*/
|
||||
export default function SignInPage() {
|
||||
const searchParams = useSearchParams();
|
||||
const callbackUrl = searchParams.get("callbackUrl") || "/";
|
||||
|
||||
useEffect(() => {
|
||||
void signIn("authentik", { callbackUrl });
|
||||
}, [callbackUrl]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen items-center justify-center bg-background">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Se redirecționează către autentificare...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
/**
|
||||
* Server-side signin route that initiates OAuth flow with Authentik.
|
||||
* Fetches CSRF token from NextAuth, POSTs to provider signin,
|
||||
* and redirects to Authentik's authorize URL — no visible page.
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
const callbackUrl =
|
||||
request.nextUrl.searchParams.get("callbackUrl") || "/";
|
||||
const baseUrl = process.env.NEXTAUTH_URL || "https://tools.beletage.ro";
|
||||
|
||||
try {
|
||||
// Use internal URL for server-to-server calls (avoid external roundtrip)
|
||||
const internalBase = `http://127.0.0.1:${process.env.PORT || "3000"}`;
|
||||
|
||||
// Step 1: Get CSRF token from NextAuth
|
||||
const csrfRes = await fetch(`${internalBase}/api/auth/csrf`, {
|
||||
headers: { cookie: request.headers.get("cookie") || "" },
|
||||
});
|
||||
const csrfData = (await csrfRes.json()) as { csrfToken: string };
|
||||
const csrfToken = csrfData.csrfToken;
|
||||
|
||||
// Merge request cookies with new CSRF cookies
|
||||
const csrfSetCookies = csrfRes.headers.getSetCookie();
|
||||
const existingCookies = request.headers.get("cookie") || "";
|
||||
const newCookiePairs = csrfSetCookies
|
||||
.map((c) => c.split(";")[0])
|
||||
.filter(Boolean);
|
||||
const mergedCookie = [existingCookies, ...newCookiePairs]
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
|
||||
// Step 2: POST to NextAuth's provider-specific signin
|
||||
const signinRes = await fetch(
|
||||
`${internalBase}/api/auth/signin/authentik`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
cookie: mergedCookie,
|
||||
},
|
||||
body: new URLSearchParams({ csrfToken, callbackUrl }).toString(),
|
||||
redirect: "manual",
|
||||
},
|
||||
);
|
||||
|
||||
// Step 3: Get the redirect URL (Authentik's authorize endpoint)
|
||||
const location = signinRes.headers.get("location");
|
||||
if (location) {
|
||||
// Build absolute URL if relative
|
||||
const redirectUrl = location.startsWith("http")
|
||||
? location
|
||||
: `${baseUrl}${location}`;
|
||||
const response = NextResponse.redirect(redirectUrl);
|
||||
|
||||
// Forward all set-cookie headers to browser (CSRF token, state, etc.)
|
||||
for (const cookie of csrfSetCookies) {
|
||||
response.headers.append("set-cookie", cookie);
|
||||
}
|
||||
for (const cookie of signinRes.headers.getSetCookie()) {
|
||||
response.headers.append("set-cookie", cookie);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("[auth/signin] Server-side redirect failed:", error);
|
||||
}
|
||||
|
||||
// Fallback: redirect to NextAuth's built-in signin page
|
||||
return NextResponse.redirect(
|
||||
`${baseUrl}/api/auth/signin?callbackUrl=${encodeURIComponent(callbackUrl)}`,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user