/** * ZDACTED waitlist backend — production build. * * A sanitized copy of this file is served at /source.txt (admin implementation * elided; every user-facing route is verbatim). The reciprocity contract: * * - Identification is X OAuth only. Scopes: users.read tweet.read — the * minimum X's API accepts for returning the authenticated username; both are * read-only. The app is registered with Read permission, so a token that * could post, DM, or follow is never issued. * - The access token is used to fetch your X id + username, then discarded: * it is never stored, never logged, and offline.access is not requested, * so no refresh token exists. * - The canonical applicant identifier is the immutable X user id. Changing * your @handle does not create a new identity. * - Stored per application: { x_user_id, x_username, ticket, created_at, * status }. No wallet address, no email, no KYC, no token. ZDACTED does not * store your IP address in the application database. * - Deleting your application hard-deletes the row. * * Bindings (see DEPLOY_CLOUDFLARE.md): D1 database DB; KV namespace KV (OAuth * state + best-effort rate limiting only — never applicant data). * Vars/secrets: X_CLIENT_ID, X_CLIENT_SECRET?, SESSION_SECRET, SITE_URL, * ADMIN_TOKEN, MAINTENANCE? ("1" enables maintenance mode). */ const HANDLE_RE = /^[A-Za-z0-9_]{1,15}$/; const XID_RE = /^[0-9]{1,25}$/; const TICKET_RE = /^ZD-[2-9A-HJ-NP-Z]{13}$/; const SESSION_MAX_AGE_S = 7 * 24 * 3600; const MAX_BODY_BYTES = 2048; const json = (data, status = 200, headers = {}) => new Response(JSON.stringify(data), { status, headers: { "content-type": "application/json", "cache-control": "no-store", ...headers }, }); /* ── crypto helpers (WebCrypto only) ── */ const te = new TextEncoder(); const b64u = (buf) => btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); const b64uToStr = (s) => atob(s.replace(/-/g, "+").replace(/_/g, "/")); async function hmac(secret, msg) { const key = await crypto.subtle.importKey("raw", te.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]); return b64u(await crypto.subtle.sign("HMAC", key, te.encode(msg))); } async function sha256b64u(s) { return b64u(await crypto.subtle.digest("SHA-256", te.encode(s))); } const rand = (n) => { const b = new Uint8Array(n); crypto.getRandomValues(b); return b64u(b); }; /** Constant-time-ish comparison: compare keyed digests, not raw strings. */ async function safeEqual(secret, a, b) { if (typeof a !== "string" || typeof b !== "string") return false; return (await hmac(secret, `cmp:${a}`)) === (await hmac(secret, `cmp:${b}`)); } /** Application ticket: ZD- + 13 chars from an unambiguous alphabet (~59 bits). */ function newTicket() { const alphabet = "23456789ABCDEFGHJKMNPQRSTUVWXYZ"; const b = new Uint8Array(13); crypto.getRandomValues(b); let s = ""; for (const x of b) s += alphabet[x % 31]; return "ZD-" + s; } /* ── signed stateless session: zdct=payload.sig ── */ async function makeSessionCookie(env, uid, username) { const payload = b64u(te.encode(JSON.stringify({ u: uid, n: username, s: rand(12), t: Date.now() }))); const sig = await hmac(env.SESSION_SECRET, `sess:${payload}`); return `zdct=${payload}.${sig}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=${SESSION_MAX_AGE_S}`; } const CLEAR_COOKIE = "zdct=; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=0"; async function getSession(env, request) { const m = /(?:^|;\s*)zdct=([^;]+)/.exec(request.headers.get("cookie") || ""); if (!m) return null; const [payload, sig] = m[1].split("."); if (!payload || !sig) return null; if ((await hmac(env.SESSION_SECRET, `sess:${payload}`)) !== sig) return null; try { const s = JSON.parse(b64uToStr(payload)); if (!XID_RE.test(String(s.u)) || !HANDLE_RE.test(String(s.n))) return null; if (Date.now() - s.t > SESSION_MAX_AGE_S * 1000) return { expired: true }; return { uid: String(s.u), username: String(s.n).toLowerCase() }; } catch { return null; } } /* ── best-effort per-IP rate limit (KV; fail-open). The hard layer is a Cloudflare WAF rate rule on /api/* — see DEPLOY_CLOUDFLARE.md. ── */ async function rateLimited(env, bucket, ip, limit, windowSec) { try { const k = `rl:${bucket}:${ip}`; const n = parseInt((await env.KV.get(k)) || "0", 10) + 1; await env.KV.put(k, String(n), { expirationTtl: windowSec }); return n > limit; } catch { return false; } } /* ── request guards ── */ function methodGuard(request, allowed) { if (!allowed.includes(request.method)) { return new Response(JSON.stringify({ error: "method_not_allowed" }), { status: 405, headers: { allow: allowed.join(", "), "content-type": "application/json", "cache-control": "no-store" }, }); } return null; } function originGuard(env, request) { const origin = request.headers.get("origin"); if (origin && env.SITE_URL && origin !== env.SITE_URL) return json({ error: "bad_origin" }, 403); return null; } function sizeGuard(request) { const cl = parseInt(request.headers.get("content-length") || "0", 10); if (cl > MAX_BODY_BYTES) return json({ error: "payload_too_large" }, 413); return null; } const log = (evt, extra = {}) => { // Aggregate operational telemetry only. Never tokens, cookies, or OAuth params. try { console.log(JSON.stringify({ evt, ...extra })); } catch {} }; export async function onRequest({ request, env, params }) { const url = new URL(request.url); const route = "/" + (params.route || []).join("/"); const ip = request.headers.get("cf-connecting-ip") || "unknown"; const t0 = Date.now(); try { if (route === "/health") { const g = methodGuard(request, ["GET"]); if (g) return g; let db = true; try { await env.DB.prepare("SELECT 1").first(); } catch { db = false; } return json({ ok: true, x_configured: !!env.X_CLIENT_ID, db, maintenance: env.MAINTENANCE === "1" }); } if (env.MAINTENANCE === "1") return json({ error: "maintenance" }, 503); /* ── X OAuth 2.0 + PKCE ───────────────────────────────────────────── */ if (route === "/auth/x/start") { const g = methodGuard(request, ["GET"]); if (g) return g; if (await rateLimited(env, "start", ip, 10, 300)) return json({ error: "rate_limited" }, 429); if (!env.X_CLIENT_ID) return json({ error: "x_not_configured" }, 503); const state = rand(16), verifier = rand(32); await env.KV.put(`oauth:${state}`, verifier, { expirationTtl: 600 }); // single-use, 10 min const p = new URLSearchParams({ response_type: "code", client_id: env.X_CLIENT_ID, redirect_uri: `${env.SITE_URL}/api/auth/x/callback`, scope: "users.read tweet.read", // X's minimum for users/me; read-only; no offline.access → no refresh token state, code_challenge: await sha256b64u(verifier), code_challenge_method: "S256", }); return Response.redirect(`https://x.com/i/oauth2/authorize?${p}`, 302); } if (route === "/auth/x/callback") { const g = methodGuard(request, ["GET"]); if (g) return g; const back = (frag) => new Response(null, { status: 302, headers: { location: `${env.SITE_URL}/#${frag}`, "cache-control": "no-store" } }); if (url.searchParams.get("error")) return back("apply-error=denied"); // user cancelled at X const code = url.searchParams.get("code"), state = url.searchParams.get("state") || ""; if (!code || !/^[A-Za-z0-9_-]{1,128}$/.test(state)) return back("apply-error=state"); const verifier = await env.KV.get(`oauth:${state}`); if (!verifier) return back("apply-error=state"); // unknown, expired, or replayed state await env.KV.delete(`oauth:${state}`); // single-use const body = new URLSearchParams({ grant_type: "authorization_code", code, client_id: env.X_CLIENT_ID, redirect_uri: `${env.SITE_URL}/api/auth/x/callback`, code_verifier: verifier, }); const headers = { "content-type": "application/x-www-form-urlencoded" }; if (env.X_CLIENT_SECRET) headers.authorization = "Basic " + btoa(`${env.X_CLIENT_ID}:${env.X_CLIENT_SECRET}`); const tokRes = await fetch("https://api.x.com/2/oauth2/token", { method: "POST", headers, body }); const tok = await tokRes.json().catch(() => ({})); if (!tok.access_token) { log("oauth_token_fail", { status: tokRes.status }); return back("apply-error=api"); } const meRes = await fetch("https://api.x.com/2/users/me", { headers: { authorization: `Bearer ${tok.access_token}` } }); const me = await meRes.json().catch(() => ({})); // access token now out of scope forever — never stored, never logged const uid = me?.data?.id, handle = me?.data?.username; if (!uid || !XID_RE.test(uid) || !handle || !HANDLE_RE.test(handle)) { log("oauth_me_fail", { status: meRes.status }); return back("apply-error=api"); } log("oauth_ok", { ms: Date.now() - t0 }); return new Response(null, { status: 302, headers: { location: `${env.SITE_URL}/#apply-authed`, "set-cookie": await makeSessionCookie(env, uid, handle.toLowerCase()), "cache-control": "no-store" }, }); } /* ── application (X-authenticated only; no wallet, no address) ────── */ if (route === "/apply") { const g = methodGuard(request, ["POST"]) || originGuard(env, request) || sizeGuard(request); if (g) return g; if (await rateLimited(env, "apply", ip, 10, 300)) return json({ error: "rate_limited" }, 429); const sess = await getSession(env, request); if (sess?.expired) return json({ error: "session_expired" }, 401); if (!sess) return json({ error: "not_authed" }, 401); for (let attempt = 0; attempt < 2; attempt++) { try { const row = await env.DB.prepare( `INSERT INTO applications (x_user_id, x_username, ticket, status) VALUES (?1, ?2, ?3, 'active') ON CONFLICT(x_user_id) DO UPDATE SET x_username = excluded.x_username RETURNING ticket, created_at, (SELECT COUNT(*) FROM applications WHERE status = 'active') AS cnt`, ).bind(sess.uid, sess.username, newTicket()).first(); log("apply_ok", { ms: Date.now() - t0 }); return json({ ok: true, ticket: row.ticket, created_at: row.created_at }); } catch (e) { if (attempt === 0 && String(e?.message || e).includes("UNIQUE") ) continue; // ticket collision → one retry throw e; } } } if (route === "/status") { const g = methodGuard(request, ["GET"]); if (g) return g; if (await rateLimited(env, "status", ip, 60, 300)) return json({ error: "rate_limited" }, 429); const sess = await getSession(env, request); if (sess?.expired) return json({ authed: false, expired: true }); if (!sess) return json({ authed: false }); const row = await env.DB.prepare( "SELECT ticket, x_username, created_at, status FROM applications WHERE x_user_id = ?1", ).bind(sess.uid).first(); return json({ authed: true, handle: sess.username, application: row ? { ticket: row.ticket, handle: row.x_username, created_at: row.created_at, status: row.status } : null, }); } if (route === "/wipe") { const g = methodGuard(request, ["POST"]) || originGuard(env, request) || sizeGuard(request); if (g) return g; if (await rateLimited(env, "wipe", ip, 5, 300)) return json({ error: "rate_limited" }, 429); const sess = await getSession(env, request); if (sess?.expired) return json({ error: "session_expired" }, 401); if (!sess) return json({ error: "not_authed" }, 401); const row = await env.DB.prepare("DELETE FROM applications WHERE x_user_id = ?1 RETURNING ticket").bind(sess.uid).first(); log("wipe", { existed: !!row }); return json({ ok: true, deleted: !!row }); } if (route === "/count") { const g = methodGuard(request, ["GET"]); if (g) return g; const row = await env.DB.prepare("SELECT COUNT(*) AS c FROM applications WHERE status = 'active'").first(); return json({ count: row.c }, 200, { "cache-control": "public, max-age=30" }); } if (route === "/logout") { const g = methodGuard(request, ["POST"]) || originGuard(env, request); if (g) return g; return json({ ok: true }, 200, { "set-cookie": CLEAR_COOKIE }); } // [admin routes elided from this transparency copy — they contain no user data // handling beyond reads of the same applications table, and no secrets; auth // is a Bearer token checked against a secret binding. Everything user-facing // above and below this block is the exact deployed code.] return json({ error: "not_found" }, 404); } catch (e) { log("server_error", { route, msg: String(e?.message || "").slice(0, 200) }); return json({ error: "server_error" }, 500); // no stack traces or bindings leak } }