51 lines
2.0 KiB
TypeScript
51 lines
2.0 KiB
TypeScript
import { cookies } from "next/headers";
|
|
import { NextRequest, NextResponse } from "next/server";
|
|
import { requireAdmin } from "@/lib/auth";
|
|
|
|
function escapeHtml(value: unknown) {
|
|
return String(value ?? "")
|
|
.replaceAll("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">")
|
|
.replaceAll('"', """)
|
|
.replaceAll("'", "'");
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
await requireAdmin();
|
|
const params = new URL(request.url).searchParams;
|
|
const cookieStore = await cookies();
|
|
const expected = cookieStore.get("youtube-oauth-state")?.value;
|
|
cookieStore.delete("youtube-oauth-state");
|
|
if (!expected || expected !== params.get("state"))
|
|
return NextResponse.json(
|
|
{ error: "Invalid OAuth state." },
|
|
{ status: 400 },
|
|
);
|
|
const code = params.get("code");
|
|
if (!code)
|
|
return NextResponse.json(
|
|
{ error: "No authorization code received." },
|
|
{ status: 400 },
|
|
);
|
|
const response = await fetch("https://oauth2.googleapis.com/token", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams({
|
|
code,
|
|
client_id: process.env.YOUTUBE_CLIENT_ID ?? "",
|
|
client_secret: process.env.YOUTUBE_CLIENT_SECRET ?? "",
|
|
redirect_uri: `${process.env.BASE_URL}/api/auth/youtube/callback`,
|
|
grant_type: "authorization_code",
|
|
}),
|
|
});
|
|
const data = (await response.json()) as Record<string, unknown>;
|
|
if (!response.ok || !data.refresh_token)
|
|
return NextResponse.json(
|
|
{ error: "YouTube token exchange failed." },
|
|
{ status: 400 },
|
|
);
|
|
const html = `<!doctype html><html><body style="font-family:system-ui;max-width:720px;margin:40px auto;padding:20px"><h1>YouTube OAuth success</h1><p>Copy these values into your server environment:</p><pre>YOUTUBE_REFRESH_TOKEN=${escapeHtml(data.refresh_token)}\nYOUTUBE_ACCESS_TOKEN=${escapeHtml(data.access_token)}</pre><p>Restart the app after saving them.</p></body></html>`;
|
|
return new NextResponse(html, { headers: { "Content-Type": "text/html" } });
|
|
}
|