import { NextRequest, NextResponse } from "next/server"; import { cookies } from "next/headers"; import crypto from "crypto"; export const dynamic = 'force-dynamic'; export async function GET(request: NextRequest) { try { const searchParams = request.nextUrl.searchParams; const guildId = searchParams.get("guild_id"); if (!guildId) { return new NextResponse("Missing guild_id", { status: 400 }); } // Generate a random state for security const state = crypto.randomBytes(16).toString("hex"); // Set the state in a secure cookie (HttpOnly, Secure, SameSite) // This cookie allows us to verify the callback originated from this flow const cookieStore = await cookies(); cookieStore.set("oauth_invite_state", state, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", maxAge: 300, // 5 minutes to complete the flow path: "/", }); // Store the target guild ID in a cookie too, to verify against the callback's guild_id // This prevents someone from starting a flow for Guild A and swapping the callback to Guild B (though state mismatch would likely catch it too) cookieStore.set("oauth_invite_guild", guildId, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "lax", maxAge: 300, path: "/", }); const appUrl = process.env.APP_URL; if (!appUrl) { console.error("APP_URL env var is not set"); return new NextResponse("Configuration Error", { status: 500 }); } const redirectUri = `${appUrl}/api/oauth/callback`; const clientId = process.env.AUTH_DISCORD_ID; // Construct Discord OAuth2 Authorization URL // We use response_type=code because we enabled "Requires OAuth2 Code Grant" for the bot const params = new URLSearchParams({ client_id: clientId as string, permissions: "8", // Administrator (as requested in original link) scope: "bot", // Add 'applications.commands' if needed, but 'bot' is the primary one here redirect_uri: redirectUri, response_type: "code", state: state, guild_id: guildId, disable_guild_select: "true", }); return NextResponse.redirect(`https://discord.com/oauth2/authorize?${params.toString()}`); } catch (error) { console.error("Failed to initiate invite flow:", error); return new NextResponse("Internal Server Error", { status: 500 }); } }