fix some issue s with cors and discord
This commit is contained in:
parent
81749d24d3
commit
bf6fed2684
4 changed files with 67 additions and 37 deletions
|
@ -1,4 +1,5 @@
|
|||
import { discordConfig } from "@config";
|
||||
import { withCors } from "@lib/cors";
|
||||
import { randomUUIDv7, redis } from "bun";
|
||||
|
||||
export class DiscordAuth {
|
||||
|
@ -6,23 +7,27 @@ export class DiscordAuth {
|
|||
#clientSecret = discordConfig.clientSecret;
|
||||
#redirectUri = discordConfig.redirectUri;
|
||||
|
||||
startOAuthRedirect(): Response {
|
||||
startOAuthRedirect(req: Request): 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,
|
||||
return withCors(
|
||||
Response.redirect(`https://discord.com/oauth2/authorize?${query}`, 302),
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
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 });
|
||||
if (!code)
|
||||
return withCors(
|
||||
Response.json({ error: "Missing code" }, { status: 400 }),
|
||||
req,
|
||||
);
|
||||
|
||||
const tokenRes = await fetch("https://discord.com/api/oauth2/token", {
|
||||
method: "POST",
|
||||
|
@ -38,7 +43,10 @@ export class DiscordAuth {
|
|||
|
||||
const tokenData: { access_token?: string } = await tokenRes.json();
|
||||
if (!tokenData.access_token)
|
||||
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
||||
return withCors(
|
||||
Response.json({ error: "Unauthorized" }, { status: 401 }),
|
||||
req,
|
||||
);
|
||||
|
||||
const userRes = await fetch("https://discord.com/api/users/@me", {
|
||||
headers: { Authorization: `Bearer ${tokenData.access_token}` },
|
||||
|
@ -48,14 +56,17 @@ export class DiscordAuth {
|
|||
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",
|
||||
return withCors(
|
||||
Response.json(
|
||||
{ message: "Authenticated" },
|
||||
{
|
||||
headers: {
|
||||
"Set-Cookie": `session=${sessionId}; HttpOnly; Path=/; Max-Age=3600; SameSite=None; Secure`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
42
src/index.ts
42
src/index.ts
|
@ -1,6 +1,7 @@
|
|||
import { DiscordAuth } from "@/discord";
|
||||
import { echo } from "@atums/echo";
|
||||
import { environment, verifyRequiredVariables } from "@config";
|
||||
import { withCors } from "@lib/cors";
|
||||
import { serve, sql } from "bun";
|
||||
|
||||
verifyRequiredVariables();
|
||||
|
@ -39,42 +40,27 @@ echo.info(`Listening on http://${environment.host}:${environment.port}`);
|
|||
|
||||
const auth = new DiscordAuth();
|
||||
|
||||
function withCors(res: Response): Response {
|
||||
const headers = new Headers(res.headers);
|
||||
headers.set("Access-Control-Allow-Origin", "*");
|
||||
headers.set(
|
||||
"Access-Control-Allow-Methods",
|
||||
"GET, POST, PUT, DELETE, OPTIONS",
|
||||
);
|
||||
headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
||||
headers.set("Access-Control-Allow-Credentials", "true");
|
||||
headers.set("Access-Control-Max-Age", "86400");
|
||||
return new Response(res.body, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
serve({
|
||||
port: environment.port,
|
||||
fetch: async (req) => {
|
||||
if (req.method === "OPTIONS") {
|
||||
const origin = req.headers.get("origin") ?? "";
|
||||
return new Response(null, {
|
||||
status: 204,
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Origin": origin,
|
||||
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "Content-Type, Authorization",
|
||||
"Access-Control-Max-Age": "86400", // 24 hours
|
||||
"Access-Control-Allow-Credentials": "true",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
Vary: "Origin",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const url = new URL(req.url);
|
||||
|
||||
if (url.pathname === "/auth/discord") return auth.startOAuthRedirect();
|
||||
if (url.pathname === "/auth/discord") return auth.startOAuthRedirect(req);
|
||||
if (url.pathname === "/auth/discord/callback")
|
||||
return auth.handleCallback(req);
|
||||
|
||||
|
@ -83,6 +69,7 @@ serve({
|
|||
if (!user)
|
||||
return withCors(
|
||||
Response.json({ error: "Unauthorized" }, { status: 401 }),
|
||||
req,
|
||||
);
|
||||
|
||||
const tz = url.searchParams.get("timezone");
|
||||
|
@ -92,6 +79,7 @@ serve({
|
|||
{ error: "Timezone parameter is required" },
|
||||
{ status: 400 },
|
||||
),
|
||||
req,
|
||||
);
|
||||
|
||||
try {
|
||||
|
@ -99,6 +87,7 @@ serve({
|
|||
} catch {
|
||||
return withCors(
|
||||
Response.json({ error: "Invalid timezone" }, { status: 400 }),
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -109,7 +98,7 @@ serve({
|
|||
SET username = EXCLUDED.username, timezone = EXCLUDED.timezone
|
||||
`;
|
||||
|
||||
return withCors(Response.json({ success: true }));
|
||||
return withCors(Response.json({ success: true }), req);
|
||||
}
|
||||
|
||||
if (url.pathname === "/get") {
|
||||
|
@ -117,6 +106,7 @@ serve({
|
|||
if (!id)
|
||||
return withCors(
|
||||
Response.json({ error: "Missing user ID" }, { status: 400 }),
|
||||
req,
|
||||
);
|
||||
|
||||
const rows = await sql`
|
||||
|
@ -126,6 +116,7 @@ serve({
|
|||
if (rows.length === 0) {
|
||||
return withCors(
|
||||
Response.json({ error: "User not found" }, { status: 404 }),
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -134,6 +125,7 @@ serve({
|
|||
user: { id, username: rows[0].username },
|
||||
timezone: rows[0].timezone,
|
||||
}),
|
||||
req,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -142,10 +134,14 @@ serve({
|
|||
if (!user)
|
||||
return withCors(
|
||||
Response.json({ error: "Unauthorized" }, { status: 401 }),
|
||||
req,
|
||||
);
|
||||
return withCors(Response.json(user));
|
||||
return withCors(Response.json(user), req);
|
||||
}
|
||||
|
||||
return withCors(Response.json({ error: "Not Found" }, { status: 404 }));
|
||||
return withCors(
|
||||
Response.json({ error: "Not Found" }, { status: 404 }),
|
||||
req,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
22
src/lib/cors.ts
Normal file
22
src/lib/cors.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
function withCors(response: Response, req: Request): Response {
|
||||
const origin = req.headers.get("origin");
|
||||
|
||||
const headers = new Headers(response.headers);
|
||||
if (origin && isAllowedOrigin(origin)) {
|
||||
headers.set("Access-Control-Allow-Origin", origin);
|
||||
headers.set("Access-Control-Allow-Credentials", "true");
|
||||
headers.set("Vary", "Origin");
|
||||
}
|
||||
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
function isAllowedOrigin(origin: string): boolean {
|
||||
return origin.endsWith(".discord.com") || origin === "https://discord.com";
|
||||
}
|
||||
|
||||
export { withCors };
|
|
@ -3,7 +3,8 @@
|
|||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"],
|
||||
"@config": ["config/index.ts"]
|
||||
"@config": ["config/index.ts"],
|
||||
"@lib/*": ["src/lib/*"]
|
||||
},
|
||||
"typeRoots": ["types", "./node_modules/@types"],
|
||||
"lib": ["ESNext", "DOM"],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue