67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
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 });
|
|
}
|
|
}
|