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