src/api/auth.js
1,879 bytes · 59 lines · capsule://quake0day/[email protected]
raw on github
import {
createSession, setSessionCookie, clearSessionCookie,
isAuthed, json, checkPasswordRateLimit
} from "../_lib/auth.js";
// POST /api/auth → login with { password }
// DELETE /api/auth → logout
// GET /api/auth → check status
export async function onRequest(context) {
const { request, env } = context;
const method = request.method.toUpperCase();
if (method === "GET") {
const ok = await isAuthed(request, env);
return json({ authenticated: ok });
}
if (method === "POST") {
if (!env.ADMIN_PASSWORD) {
return json({ error: "ADMIN_PASSWORD env var is not set on the server" }, { status: 500 });
}
const ip = request.headers.get("CF-Connecting-IP") || "unknown";
const rl = await checkPasswordRateLimit(env, ip);
if (!rl.ok) {
return json({ error: `Too many attempts. Try again in ${rl.retryAfter}s.` }, { status: 429 });
}
let body;
try { body = await request.json(); } catch { body = {}; }
const password = (body.password || "").toString();
if (!password) return json({ error: "Missing password" }, { status: 400 });
// Constant-time-ish compare
const a = password;
const b = env.ADMIN_PASSWORD;
let match = a.length === b.length;
const len = Math.max(a.length, b.length);
let mismatch = a.length ^ b.length;
for (let i = 0; i < len; i++) {
mismatch |= (a.charCodeAt(i) || 0) ^ (b.charCodeAt(i) || 0);
}
match = mismatch === 0;
if (!match) return json({ error: "Wrong password" }, { status: 401 });
const token = await createSession(env);
return json({ ok: true }, {
headers: { "Set-Cookie": setSessionCookie(token) }
});
}
if (method === "DELETE") {
return json({ ok: true }, {
headers: { "Set-Cookie": clearSessionCookie() }
});
}
return json({ error: "Method not allowed" }, { status: 405 });
}