mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
initial commit
This commit is contained in:
commit
512d4b01d5
55 changed files with 6430 additions and 0 deletions
82
src/oauth-routes.ts
Normal file
82
src/oauth-routes.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { Hono } from "hono";
|
||||
import { getOAuthURL, handleOAuthCallback } from "./github-oauth";
|
||||
import { removeToken } from "./token-store";
|
||||
import type { Env } from "./types";
|
||||
|
||||
interface PendingState {
|
||||
redirectTo: string;
|
||||
expiresAt: number;
|
||||
}
|
||||
|
||||
function generateRandomHex(length: number): string {
|
||||
const bytes = new Uint8Array(length);
|
||||
crypto.getRandomValues(bytes);
|
||||
return Array.from(bytes)
|
||||
.map((b) => b.toString(16).padStart(2, "0"))
|
||||
.join("");
|
||||
}
|
||||
|
||||
export function createOAuthRoutes(): Hono<{ Bindings: Env }> {
|
||||
const app = new Hono<{ Bindings: Env }>();
|
||||
|
||||
app.get("/github", async (c) => {
|
||||
const redirectTo = c.req.query("redirect") ?? "/";
|
||||
const state = generateRandomHex(16);
|
||||
|
||||
const pending: PendingState = {
|
||||
redirectTo,
|
||||
expiresAt: Date.now() + 10 * 60 * 1000,
|
||||
};
|
||||
await c.env.KV.put(`state:${state}`, JSON.stringify(pending), {
|
||||
expirationTtl: 600,
|
||||
});
|
||||
|
||||
const url = getOAuthURL(c.env.GITHUB_CLIENT_ID ?? "", state);
|
||||
return c.redirect(url);
|
||||
});
|
||||
|
||||
app.get("/github/callback", async (c) => {
|
||||
const code = c.req.query("code");
|
||||
const state = c.req.query("state");
|
||||
|
||||
if (!code || !state) {
|
||||
return c.json({ error: "Missing code or state" }, 400);
|
||||
}
|
||||
|
||||
const raw = await c.env.KV.get(`state:${state}`, "json");
|
||||
if (!raw) {
|
||||
return c.json({ error: "Invalid or expired state" }, 400);
|
||||
}
|
||||
const pending = raw as PendingState;
|
||||
if (Date.now() > pending.expiresAt) {
|
||||
await c.env.KV.delete(`state:${state}`);
|
||||
return c.json({ error: "Invalid or expired state" }, 400);
|
||||
}
|
||||
await c.env.KV.delete(`state:${state}`);
|
||||
|
||||
const result = await handleOAuthCallback(
|
||||
c.env.GITHUB_CLIENT_ID ?? "",
|
||||
c.env.GITHUB_CLIENT_SECRET ?? "",
|
||||
code,
|
||||
state,
|
||||
c.env.KV,
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
return c.json({ error: "OAuth failed" }, 400);
|
||||
}
|
||||
|
||||
return c.json({
|
||||
userId: result.userId,
|
||||
login: result.login,
|
||||
redirectTo: pending.redirectTo,
|
||||
});
|
||||
});
|
||||
|
||||
app.delete("/token/:userId", async (c) => {
|
||||
await removeToken(c.env.KV, c.req.param("userId"));
|
||||
return c.json({ ok: true });
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue