From 1d87d0d36feea526f8f6bf4a62b012132bd36128 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 14 Sep 2026 12:54:23 -0700 Subject: register: start the session from the register response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /register now verifies the recaptcha token itself, and Google tokens are single-use — so the follow-up /login that reused the same token came back "failed captcha" for an account that had just been created. The register response already carries the JWT; set the session cookies from it and only fall back to /login when registration itself failed. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_0166Ci2XdCair1B9ych7w8dq --- src/lib/utils.ts | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'src/lib') diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 14a1370e..b6bbdb53 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -172,8 +172,6 @@ export const login = async ( host?: string, extraHeaders?: Record, ) => { - const maxAge = 380 * 24 * 60 * 60; - const headers: Record = { "content-type": "application/json", accept: "application/json", @@ -198,6 +196,18 @@ export const login = async ( const { user: u, token } = JSON.parse(text); if (!token) throw new Error("Login failed"); + setSession(cookies, user, u, token); +}; + +// Session cookies for a freshly issued JWT — shared by login and register, +// which both get a token back from the server. +const setSession = ( + cookies, + user: { username: string; password: string }, + u: { language?: string; nsec?: string }, + token: string, +) => { + const maxAge = 380 * 24 * 60 * 60; const expires = new Date(); expires.setSeconds(expires.getSeconds() + maxAge); @@ -507,19 +517,30 @@ export const register = async (user, ip, cookies, loginRedirect, host?, extraHea }; if (ip) headers["cf-connecting-ip"] = ip; - let sk; + let sk, token, u; try { - ({ sk } = await post("/register", { user }, headers)); + ({ sk, token, ...u } = await post("/register", { user }, headers)); } catch (e) { ({ message: error } = e as Error); } - try { - await login(user, cookies, ip, host, extraHeaders); + // /register verifies the recaptcha token itself now, and Google tokens + // are single-use: a follow-up /login carrying the same token comes back + // "failed captcha" for an account that was just created. The register + // response already includes the JWT, so start the session from it and + // only fall back to /login when registration failed (e.g. the username + // already exists and this is really a login). + if (token) { + setSession(cookies, user, u, token); error = null; - } catch (e) { - const { message } = e as Error; - error ||= message; + } else { + try { + await login(user, cookies, ip, host, extraHeaders); + error = null; + } catch (e) { + const { message } = e as Error; + error ||= message; + } } const expires = new Date(); -- cgit v1.2.3