From 7258a5bd8a3faee0627d27bcb4d8e9884a4cb82d Mon Sep 17 00:00:00 2001 From: Kenneth Hough Date: Wed, 3 Jun 2026 04:24:20 -0500 Subject: [PATCH] feat: anonymous fall-through for JWT-mode public access (KYTE-#229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a JWT-mode client has no access token AND no refresh token (a visitor who never logged in), _jwtEnsureFreshAccessToken now issues the request anonymously (x-kyte-appid only, no Authorization header) instead of failing closed. This enables public/anonymous browsing of requireAuth=false controllers served by the server's new AppContextStrategy. A present-but-stale access token with no refresh token still fails so the caller destroys state + bounces to login (unchanged). issueRequest already omits the Authorization header when no token is present and always sends x-kyte-appid, so the anonymous request shape was already supported — it was just unreachable. Pairs with kyte-php v4.11.0 (KYTE-#229). --- kyte-source.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/kyte-source.js b/kyte-source.js index 26a7a38..ea524e0 100644 --- a/kyte-source.js +++ b/kyte-source.js @@ -930,7 +930,22 @@ class Kyte { } if (!obj.jwtRefreshToken) { - onFail({ error: 'no_refresh_token', message: 'JWT session has no refresh token.' }); + // No refresh token. Two distinct cases: + // 1. No access token either → an ANONYMOUS visitor who never + // logged in. Issue the request anyway: issueRequest sets the + // Authorization header only when a token exists and always + // sends x-kyte-appid, so this fires an app-only request that + // the server's AppContextStrategy serves for requireAuth=false + // (public) controllers. This is what enables JWT-mode public + // browsing before login. + // 2. Had an access token but no/lost refresh token → a broken or + // expired session. Fail so the caller destroys state + bounces + // to login (unchanged behavior). + if (!obj.jwtAccessToken) { + onReady(); + } else { + onFail({ error: 'no_refresh_token', message: 'JWT session has no refresh token.' }); + } return; }