A discord bot for teal.fm
discord tealfm music

more auth work, some validation stuff

besaid.zone 703bc015 09091851

verified
Changed files
+60 -26
apps
+44 -16
apps/web/index.ts
··· 1 import { serve, type HttpBindings } from "@hono/node-server"; 2 import { logger } from "@tealfmbot/common/logger.js"; 3 import { Hono } from "hono"; 4 import pinoHttpLogger from "pino-http"; 5 6 import { client } from "./client"; 7 import { createSession, MAX_AGE } from "./utils"; 8 - import { getSignedCookie } from "hono/cookie"; 9 - import { COOKIE_SECRET } from "@tealfmbot/common/constants.js"; 10 11 type Variables = { 12 logger: typeof logger; ··· 27 }); 28 29 app.get("/", (c) => { 30 - c.var.logger.info("test log"); 31 return c.text("yo!!"); 32 }); 33 34 app.get("/oauth-client-metadata.json", (c) => { 35 c.header("Cache-Control", `max-age=${MAX_AGE}, public`); 36 return c.json(client.clientMetadata); ··· 42 }); 43 44 app.get("/oauth/callback", async (c) => { 45 - c.header("Cache-Control", "no-store") 46 - const params = new URLSearchParams(c.req.url.split("?")[1]) 47 48 try { 49 - const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session") 50 if (session) { 51 try { 52 - const oauthSession = await client.restore(session) 53 - if (oauthSession) oauthSession.signOut() 54 - } catch { 55 - logger.warn("oauth restore failed") 56 } 57 } 58 - const oauth = await client.callback(params) 59 await createSession(oauth.session.did); 60 - 61 - } catch { 62 - logger.error("oauth callback failed") 63 } 64 65 - return c.redirect("/") 66 - }) 67 68 const server = serve({ 69 fetch: app.fetch,
··· 1 + import { isValidHandle } from "@atproto/syntax"; 2 import { serve, type HttpBindings } from "@hono/node-server"; 3 + import { COOKIE_SECRET } from "@tealfmbot/common/constants.js"; 4 import { logger } from "@tealfmbot/common/logger.js"; 5 import { Hono } from "hono"; 6 + import { getSignedCookie } from "hono/cookie"; 7 + import { validator } from "hono/validator"; 8 import pinoHttpLogger from "pino-http"; 9 10 import { client } from "./client"; 11 import { createSession, MAX_AGE } from "./utils"; 12 13 type Variables = { 14 logger: typeof logger; ··· 29 }); 30 31 app.get("/", (c) => { 32 return c.text("yo!!"); 33 }); 34 35 + app.get("/health", (c) => { 36 + return c.json({ message: "OK" }, 200); 37 + }); 38 + 39 app.get("/oauth-client-metadata.json", (c) => { 40 c.header("Cache-Control", `max-age=${MAX_AGE}, public`); 41 return c.json(client.clientMetadata); ··· 47 }); 48 49 app.get("/oauth/callback", async (c) => { 50 + c.header("Cache-Control", "no-store"); 51 + const params = new URLSearchParams(c.req.url.split("?")[1]); 52 53 try { 54 + const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session"); 55 if (session) { 56 try { 57 + const oauthSession = await client.restore(session); 58 + if (oauthSession) oauthSession.signOut(); 59 + } catch (error) { 60 + logger.warn({ error }, "oauth restore failed"); 61 } 62 } 63 + const oauth = await client.callback(params); 64 await createSession(oauth.session.did); 65 + } catch (error) { 66 + logger.error({ error }, "oauth callback failed"); 67 } 68 69 + return c.redirect("/"); 70 + }); 71 + 72 + app.post( 73 + "/login", 74 + validator("form", (value, c) => { 75 + const identifier = value["identifier"]; 76 + if (!identifier || typeof identifier !== "string" || !isValidHandle(identifier)) { 77 + return c.json({ message: "Invalid handle, did or PDS URL" }, 400); 78 + } 79 + 80 + return { 81 + identifier, 82 + }; 83 + }), 84 + (c) => { 85 + c.header("Cache-Control", "no-store"); 86 + const { identifier } = c.req.valid("form"); 87 + try { 88 + return c.text(identifier); 89 + } catch (error) { 90 + logger.error({ error }, "oauth authorize failed"); 91 + return c.json({ message: "oauth authorize failed" }, 500); 92 + } 93 + }, 94 + ); 95 96 const server = serve({ 97 fetch: app.fetch,
+1
apps/web/package.json
··· 11 "dependencies": { 12 "@atproto/api": "^0.18.8", 13 "@atproto/oauth-client-node": "^0.3.13", 14 "@hono/node-server": "^1.19.7", 15 "@tealfmbot/common": "workspace:*", 16 "@tealfmbot/database": "workspace:*",
··· 11 "dependencies": { 12 "@atproto/api": "^0.18.8", 13 "@atproto/oauth-client-node": "^0.3.13", 14 + "@atproto/syntax": "^0.4.2", 15 "@hono/node-server": "^1.19.7", 16 "@tealfmbot/common": "workspace:*", 17 "@tealfmbot/database": "workspace:*",
+12 -10
apps/web/utils.ts
··· 1 - import { COOKIE_SECRET } from "@tealfmbot/common/constants.ts"; 2 import type { Context } from "hono"; 3 - import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie"; 4 - import { client } from "./client"; 5 import { Agent } from "@atproto/api"; 6 import { logger } from "@tealfmbot/common/logger.js"; 7 8 export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0; 9 ··· 19 } 20 21 export async function getSessionAgent(c: Context) { 22 - c.header("Vary", "Cookie") 23 - const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session") 24 if (!session) return null; 25 - c.header("Cache-Control", `max-age=${MAX_AGE}, private`) 26 27 try { 28 - const oauthSession = await client.restore(session) 29 - return oauthSession ? new Agent(oauthSession) : null 30 } catch { 31 - logger.warn("oauth restore failed") 32 - deleteCookie(c, "__teal_fm_bot_session") 33 return null; 34 } 35 }
··· 1 import type { Context } from "hono"; 2 + 3 import { Agent } from "@atproto/api"; 4 + import { COOKIE_SECRET } from "@tealfmbot/common/constants.ts"; 5 import { logger } from "@tealfmbot/common/logger.js"; 6 + import { deleteCookie, generateSignedCookie, getSignedCookie } from "hono/cookie"; 7 + 8 + import { client } from "./client"; 9 10 export const MAX_AGE = process.env.NODE_ENV === "production" ? 60 : 0; 11 ··· 21 } 22 23 export async function getSessionAgent(c: Context) { 24 + c.header("Vary", "Cookie"); 25 + const session = await getSignedCookie(c, COOKIE_SECRET, "__teal_fm_bot_session"); 26 if (!session) return null; 27 + c.header("Cache-Control", `max-age=${MAX_AGE}, private`); 28 29 try { 30 + const oauthSession = await client.restore(session); 31 + return oauthSession ? new Agent(oauthSession) : null; 32 } catch { 33 + logger.warn("oauth restore failed"); 34 + deleteCookie(c, "__teal_fm_bot_session"); 35 return null; 36 } 37 }
+3
pnpm-lock.yaml
··· 73 '@atproto/oauth-client-node': 74 specifier: ^0.3.13 75 version: 0.3.13 76 '@hono/node-server': 77 specifier: ^1.19.7 78 version: 1.19.7(hono@4.11.3)
··· 73 '@atproto/oauth-client-node': 74 specifier: ^0.3.13 75 version: 0.3.13 76 + '@atproto/syntax': 77 + specifier: ^0.4.2 78 + version: 0.4.2 79 '@hono/node-server': 80 specifier: ^1.19.7 81 version: 1.19.7(hono@4.11.3)