Create Roblox OAuth callback handler
cgen-69b8987cff38461d9c00d268861cf6bb
This commit is contained in:
parent
c9a5cd2593
commit
ed0f77ca7c
1 changed files with 88 additions and 0 deletions
88
api/roblox/oauth-callback.ts
Normal file
88
api/roblox/oauth-callback.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import type { VercelRequest, VercelResponse } from "@vercel/node";
|
||||
|
||||
interface RobloxTokenResponse {
|
||||
access_token: string;
|
||||
token_type: string;
|
||||
expires_in: number;
|
||||
refresh_token?: string;
|
||||
}
|
||||
|
||||
interface RobloxUserInfo {
|
||||
sub: string;
|
||||
preferred_username: string;
|
||||
name: string;
|
||||
email?: string;
|
||||
picture?: string;
|
||||
}
|
||||
|
||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||
if (req.method !== "POST") {
|
||||
res.setHeader("Allow", "POST");
|
||||
return res.status(405).json({ error: "Method not allowed" });
|
||||
}
|
||||
|
||||
try {
|
||||
const { code, state } = req.body;
|
||||
|
||||
if (!code) {
|
||||
return res.status(400).json({ error: "Authorization code is required" });
|
||||
}
|
||||
|
||||
const clientId = process.env.ROBLOX_OAUTH_CLIENT_ID;
|
||||
const clientSecret = process.env.ROBLOX_OAUTH_CLIENT_SECRET;
|
||||
const redirectUri =
|
||||
process.env.ROBLOX_OAUTH_REDIRECT_URI || "https://aethex.dev/roblox-callback";
|
||||
|
||||
if (!clientId || !clientSecret) {
|
||||
return res.status(500).json({ error: "Roblox OAuth not configured" });
|
||||
}
|
||||
|
||||
// Exchange code for token
|
||||
const tokenResponse = await fetch("https://apis.roblox.com/oauth/token", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: new URLSearchParams({
|
||||
grant_type: "authorization_code",
|
||||
code,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
redirect_uri: redirectUri,
|
||||
}).toString(),
|
||||
});
|
||||
|
||||
if (!tokenResponse.ok) {
|
||||
const errorData = await tokenResponse.text();
|
||||
console.error("Roblox token exchange failed:", errorData);
|
||||
return res.status(401).json({ error: "Token exchange failed" });
|
||||
}
|
||||
|
||||
const tokenData: RobloxTokenResponse = await tokenResponse.json();
|
||||
|
||||
// Get user info with access token
|
||||
const userResponse = await fetch("https://apis.roblox.com/oauth/v1/userinfo", {
|
||||
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
||||
});
|
||||
|
||||
if (!userResponse.ok) {
|
||||
console.error("Failed to fetch Roblox user info");
|
||||
return res.status(401).json({ error: "Failed to fetch user info" });
|
||||
}
|
||||
|
||||
const userInfo: RobloxUserInfo = await userResponse.json();
|
||||
|
||||
// Return user info to frontend
|
||||
return res.status(200).json({
|
||||
roblox_user_id: userInfo.sub,
|
||||
roblox_username: userInfo.preferred_username,
|
||||
roblox_name: userInfo.name,
|
||||
roblox_email: userInfo.email || null,
|
||||
roblox_avatar: userInfo.picture || null,
|
||||
state,
|
||||
});
|
||||
} catch (error: any) {
|
||||
console.error("Roblox OAuth callback error:", error);
|
||||
return res.status(500).json({
|
||||
error: error?.message || "Authentication failed",
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue