79 lines
2.1 KiB
TypeScript
79 lines
2.1 KiB
TypeScript
import { discordConfig } from "@config";
|
|
import { randomUUIDv7, redis } from "bun";
|
|
|
|
export class DiscordAuth {
|
|
#clientId = discordConfig.clientId;
|
|
#clientSecret = discordConfig.clientSecret;
|
|
#redirectUri = discordConfig.redirectUri;
|
|
|
|
startOAuthRedirect(): Response {
|
|
const query = new URLSearchParams({
|
|
client_id: this.#clientId,
|
|
redirect_uri: this.#redirectUri,
|
|
response_type: "code",
|
|
scope: "identify",
|
|
});
|
|
return Response.redirect(
|
|
`https://discord.com/oauth2/authorize?${query}`,
|
|
302,
|
|
);
|
|
}
|
|
|
|
async handleCallback(req: Request): Promise<Response> {
|
|
const url = new URL(req.url);
|
|
const code = url.searchParams.get("code");
|
|
if (!code) return Response.json({ error: "Missing code" }, { status: 400 });
|
|
|
|
const tokenRes = await fetch("https://discord.com/api/oauth2/token", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
body: new URLSearchParams({
|
|
client_id: this.#clientId,
|
|
client_secret: this.#clientSecret,
|
|
grant_type: "authorization_code",
|
|
code,
|
|
redirect_uri: this.#redirectUri,
|
|
}),
|
|
});
|
|
|
|
const tokenData: { access_token?: string } = await tokenRes.json();
|
|
if (!tokenData.access_token)
|
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
|
|
const userRes = await fetch("https://discord.com/api/users/@me", {
|
|
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
|
});
|
|
const user: DiscordUser = await userRes.json();
|
|
|
|
const sessionId = randomUUIDv7();
|
|
await redis.set(sessionId, JSON.stringify(user), "EX", 3600);
|
|
|
|
return Response.json(
|
|
{ message: "Authenticated" },
|
|
{
|
|
headers: {
|
|
"Set-Cookie": `session=${sessionId}; HttpOnly; Path=/; Max-Age=3600`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
async getUser(req: Request): Promise<DiscordUser | null> {
|
|
const cookie = req.headers.get("cookie");
|
|
if (!cookie) return null;
|
|
|
|
const match = cookie.match(/session=([^;]+)/);
|
|
if (!match) return null;
|
|
|
|
const sessionId = match[1];
|
|
const userData = await redis.get(sessionId);
|
|
if (!userData) return null;
|
|
|
|
try {
|
|
return JSON.parse(userData);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
}
|