bliss-cli: detect failed logins instead of caching anon sessions

The server issues a connect.sid cookie on every response, including a
rejected login (which re-renders the form as 200). login() treated any
returned cookie as success, so bad creds silently cached an anonymous
session and the CLI stayed logged out with no warning — and since it only
re-authenticates on a 401 (which the server never sends), the stale cookie
was never refreshed.

Trust the redirect status instead (302 = success, 2xx = rejected) and throw
a clear error with the HTTP code on failure. Add a `bliss login` command
that forces a fresh login past any stale cookie.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-08-21 10:17:28 -04:00
parent 02cdca748d
commit e7064a7c6a
2 changed files with 23 additions and 3 deletions

View file

@ -68,6 +68,14 @@ const commands = {
out(c.BASE); out(c.BASE);
} }
}, },
// Force a fresh login with BLISS_USER / BLISS_PASS and report the result.
// Unlike normal commands (which reuse a cached cookie and only re-auth on a
// 401 the server never sends), this always hits /login, so it's the way to
// (re)authenticate after changing creds or clearing a stale anonymous cookie.
async login() {
await c.login(); // throws with a clear message if the server rejects creds
out("logged in as '" + (process.env.BLISS_USER || "?") + "' -> " + c.BASE);
},
// ---- reads (JSON via /plumbing) ---- // ---- reads (JSON via /plumbing) ----
async structures() { async structures() {
out(await c.getJSON("/plumbing/structures")); out(await c.getJSON("/plumbing/structures"));

View file

@ -79,10 +79,21 @@ async function login() {
headers: { "content-type": "application/x-www-form-urlencoded" }, headers: { "content-type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({ username: USER, password: PASS }).toString(), body: new URLSearchParams({ username: USER, password: PASS }).toString(),
}); });
// The server issues an (anonymous) connect.sid on EVERY response — including a
// failed login, which re-renders the form as a 200. So the presence of a
// cookie says nothing about success. The real signal is the redirect: a good
// login 302s to `next`/"/", a rejected one stays 2xx. Trust the status, not
// the cookie, or we'd cache an anonymous session and silently stay logged out.
const cookie = extractCookie(res); const cookie = extractCookie(res);
if (!cookie) { const redirected = res.status >= 300 && res.status < 400;
// A 200 back from /login means the login form re-rendered => bad creds. if (!redirected || !cookie) {
throw new Error("login failed for user '" + USER + "' (check BLISS_PASS)"); throw new Error(
"login failed for user '" +
USER +
"' — server rejected the credentials (HTTP " +
res.status +
"). Check BLISS_USER / BLISS_PASS.",
);
} }
saveCookie(cookie); saveCookie(cookie);
return cookie; return cookie;
@ -168,6 +179,7 @@ async function getText(urlPath) {
module.exports = { module.exports = {
BASE, BASE,
setTarget, setTarget,
login,
request, request,
getJSON, getJSON,
getText, getText,