Refactor Discord OAuth callback to POST endpoint
cgen-ff73141468f14cce8d32c10c5976d352
This commit is contained in:
parent
0c65234087
commit
48e47d9d66
1 changed files with 69 additions and 87 deletions
|
|
@ -20,19 +20,14 @@ interface DiscordTokenResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function handler(req: any, res: any) {
|
export default async function handler(req: any, res: any) {
|
||||||
if (req.method !== "GET") {
|
if (req.method !== "POST") {
|
||||||
return res.status(405).json({ error: "Method not allowed" });
|
return res.status(405).json({ error: "Method not allowed" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { code, state, error } = req.query;
|
const { code, state } = req.body;
|
||||||
|
|
||||||
// Handle Discord error
|
|
||||||
if (error) {
|
|
||||||
return res.redirect(`/login?error=${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!code) {
|
if (!code) {
|
||||||
return res.redirect("/login?error=no_code");
|
return res.status(400).json({ message: "No authorization code provided" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientId = process.env.DISCORD_CLIENT_ID;
|
const clientId = process.env.DISCORD_CLIENT_ID;
|
||||||
|
|
@ -42,34 +37,33 @@ export default async function handler(req: any, res: any) {
|
||||||
|
|
||||||
if (!clientId || !clientSecret || !supabaseUrl || !supabaseServiceRole) {
|
if (!clientId || !clientSecret || !supabaseUrl || !supabaseServiceRole) {
|
||||||
console.error("[Discord OAuth] Missing environment variables");
|
console.error("[Discord OAuth] Missing environment variables");
|
||||||
return res.redirect("/login?error=config");
|
return res.status(500).json({ message: "Server configuration error" });
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const redirectUri = `${process.env.VITE_API_BASE || "https://aethex.dev"}/discord`;
|
const redirectUri = `${process.env.VITE_API_BASE || "https://aethex.dev"}/discord`;
|
||||||
|
|
||||||
// Exchange code for access token
|
// Exchange code for access token
|
||||||
const tokenResponse = await fetch(
|
const tokenResponse = await fetch("https://discord.com/api/v10/oauth2/token", {
|
||||||
"https://discord.com/api/v10/oauth2/token",
|
method: "POST",
|
||||||
{
|
headers: {
|
||||||
method: "POST",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
|
||||||
},
|
|
||||||
body: new URLSearchParams({
|
|
||||||
client_id: clientId,
|
|
||||||
client_secret: clientSecret,
|
|
||||||
grant_type: "authorization_code",
|
|
||||||
code,
|
|
||||||
redirect_uri: redirectUri,
|
|
||||||
}).toString(),
|
|
||||||
},
|
},
|
||||||
);
|
body: new URLSearchParams({
|
||||||
|
client_id: clientId,
|
||||||
|
client_secret: clientSecret,
|
||||||
|
grant_type: "authorization_code",
|
||||||
|
code,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
}).toString(),
|
||||||
|
});
|
||||||
|
|
||||||
if (!tokenResponse.ok) {
|
if (!tokenResponse.ok) {
|
||||||
const errorData = await tokenResponse.json();
|
const errorData = await tokenResponse.json();
|
||||||
console.error("[Discord OAuth] Token exchange failed:", errorData);
|
console.error("[Discord OAuth] Token exchange failed:", errorData);
|
||||||
return res.redirect("/login?error=token_exchange");
|
return res.status(400).json({
|
||||||
|
message: "Failed to authenticate with Discord. Please try again.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const tokenData: DiscordTokenResponse = await tokenResponse.json();
|
const tokenData: DiscordTokenResponse = await tokenResponse.json();
|
||||||
|
|
@ -83,7 +77,9 @@ export default async function handler(req: any, res: any) {
|
||||||
|
|
||||||
if (!userResponse.ok) {
|
if (!userResponse.ok) {
|
||||||
console.error("[Discord OAuth] User fetch failed:", userResponse.status);
|
console.error("[Discord OAuth] User fetch failed:", userResponse.status);
|
||||||
return res.redirect("/login?error=user_fetch");
|
return res.status(400).json({
|
||||||
|
message: "Failed to fetch Discord profile. Please try again.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const discordUser: DiscordUser = await userResponse.json();
|
const discordUser: DiscordUser = await userResponse.json();
|
||||||
|
|
@ -118,47 +114,38 @@ export default async function handler(req: any, res: any) {
|
||||||
} else {
|
} else {
|
||||||
// Create new user
|
// Create new user
|
||||||
// First create auth user
|
// First create auth user
|
||||||
const { data: authData, error: authError } =
|
const { data: authData, error: authError } = await supabase.auth.admin.createUser({
|
||||||
await supabase.auth.admin.createUser({
|
email: discordUser.email,
|
||||||
email: discordUser.email,
|
email_confirm: true,
|
||||||
email_confirm: true,
|
user_metadata: {
|
||||||
user_metadata: {
|
full_name: discordUser.username,
|
||||||
full_name: discordUser.username,
|
avatar_url: discordUser.avatar
|
||||||
avatar_url: discordUser.avatar
|
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
: null,
|
||||||
: null,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (authError || !authData.user) {
|
if (authError || !authData.user) {
|
||||||
console.error(
|
console.error("[Discord OAuth] Auth user creation failed:", authError);
|
||||||
"[Discord OAuth] Auth user creation failed:",
|
return res.status(500).json({ message: "Failed to create account" });
|
||||||
authError,
|
|
||||||
);
|
|
||||||
return res.redirect("/login?error=auth_create");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
userId = authData.user.id;
|
userId = authData.user.id;
|
||||||
isNewUser = true;
|
isNewUser = true;
|
||||||
|
|
||||||
// Create user profile
|
// Create user profile
|
||||||
const { error: profileError } = await supabase
|
const { error: profileError } = await supabase.from("user_profiles").insert({
|
||||||
.from("user_profiles")
|
id: userId,
|
||||||
.insert({
|
email: discordUser.email,
|
||||||
id: userId,
|
full_name: discordUser.username,
|
||||||
email: discordUser.email,
|
avatar_url: discordUser.avatar
|
||||||
full_name: discordUser.username,
|
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
||||||
avatar_url: discordUser.avatar
|
: null,
|
||||||
? `https://cdn.discordapp.com/avatars/${discordUser.id}/${discordUser.avatar}.png`
|
});
|
||||||
: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (profileError) {
|
if (profileError) {
|
||||||
console.error(
|
console.error("[Discord OAuth] Profile creation failed:", profileError);
|
||||||
"[Discord OAuth] Profile creation failed:",
|
return res.status(500).json({ message: "Failed to create user profile" });
|
||||||
profileError,
|
|
||||||
);
|
|
||||||
return res.redirect("/login?error=profile_create");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -172,45 +159,40 @@ export default async function handler(req: any, res: any) {
|
||||||
|
|
||||||
if (linkError) {
|
if (linkError) {
|
||||||
console.error("[Discord OAuth] Link creation failed:", linkError);
|
console.error("[Discord OAuth] Link creation failed:", linkError);
|
||||||
return res.redirect("/login?error=link_create");
|
return res.status(500).json({ message: "Failed to link Discord account" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate session token
|
// Generate session token
|
||||||
const { data: sessionData, error: sessionError } =
|
const { data: sessionData, error: sessionError } = await supabase.auth.admin.createSession({
|
||||||
await supabase.auth.admin.createSession({
|
user_id: userId,
|
||||||
user_id: userId,
|
});
|
||||||
});
|
|
||||||
|
|
||||||
if (sessionError || !sessionData.session) {
|
if (sessionError || !sessionData.session) {
|
||||||
console.error("[Discord OAuth] Session creation failed:", sessionError);
|
console.error("[Discord OAuth] Session creation failed:", sessionError);
|
||||||
return res.redirect("/login?error=session_create");
|
return res.status(500).json({ message: "Failed to create session" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to next page with session
|
// Return session data to frontend
|
||||||
const nextPath =
|
res.setHeader("Content-Type", "application/json");
|
||||||
state && typeof state === "string" && state.startsWith("/")
|
return res.status(200).json({
|
||||||
? state
|
success: true,
|
||||||
: isNewUser
|
message: isNewUser ? "Account created successfully" : "Linked successfully",
|
||||||
? "/onboarding"
|
session: {
|
||||||
: "/dashboard";
|
access_token: sessionData.session.access_token,
|
||||||
const redirectUrl = new URL(
|
refresh_token: sessionData.session.refresh_token,
|
||||||
nextPath,
|
},
|
||||||
process.env.VITE_API_BASE || "https://aethex.dev",
|
user: {
|
||||||
);
|
id: userId,
|
||||||
|
email: discordUser.email,
|
||||||
// Set cookies for session (similar to how Supabase does it)
|
full_name: discordUser.username,
|
||||||
res.setHeader(
|
discord_id: discordUser.id,
|
||||||
"Set-Cookie",
|
},
|
||||||
`sb-access-token=${sessionData.session.access_token}; Path=/; HttpOnly; Secure; SameSite=Lax`,
|
isNewUser,
|
||||||
);
|
});
|
||||||
res.setHeader(
|
|
||||||
"Set-Cookie",
|
|
||||||
`sb-refresh-token=${sessionData.session.refresh_token}; Path=/; HttpOnly; Secure; SameSite=Lax`,
|
|
||||||
);
|
|
||||||
|
|
||||||
res.redirect(redirectUrl.toString());
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[Discord OAuth] Callback error:", error);
|
console.error("[Discord OAuth] Callback error:", error);
|
||||||
res.redirect("/login?error=unknown");
|
res.status(500).json({
|
||||||
|
message: "An unexpected error occurred. Please try again.",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue