Summary
keystatic-gh-access-token is stored in a non-HttpOnly cookie because the browser reads it directly when making GitHub API calls. The problem is that some enterprise firewalls (Fortinet being a common one) automatically add HttpOnly to all cookies going outside of the network, which breaks the Keystatic admin UI since JavaScript can no longer access the token.
Problem
The token is set like this:
|
[ |
|
'Set-Cookie', |
|
cookie.serialize('keystatic-gh-access-token', tokenData.access_token, { |
|
sameSite: 'lax', |
|
secure: process.env.NODE_ENV === 'production', |
|
maxAge: tokenData.expires_in, |
|
expires: new Date(Date.now() + tokenData.expires_in * 1000), |
|
path: '/', |
|
}), |
|
], |
The token is read like this:
|
const cookies = parse(document.cookie); |
|
const accessToken = cookies['keystatic-gh-access-token']; |
httpOnly is intentionally left out so the frontend can read the token and authenticate GitHub API requests. But behind certain network firewalls, that choice gets overridden, the cookie gets HttpOnly stamped onto it, and things break. There's no workaround available to the application once that happens.
Solutions
Option 1: localStorage fallback (opt-in flag)
Add a config flag in keystatic.config.ts under storage if kind is github that switches token storage from a cookie to localStorage, similar to how Keystatic Cloud auth already works (localStorage.getItem('keystatic-cloud-access-token')). Users in restricted environments could opt into this, leaving the default behavior unchanged.
For Example:
{
"storage": {
"kind": "github",
"repo": "thinkmill/keystatic",
"useLocalStorageForAccessToken": true
}
}
Option 2: Server-side GitHub proxy (preferred)
Route GitHub API requests through Keystatic's existing API routes instead of making them directly from the browser:
Browser → Keystatic API → GitHub API
The browser never touches the token, so keystatic-gh-access-token can be HttpOnly (or dropped from the client entirely). This also closes off client-side token exposure as a side benefit.
Contribution
If the issue looks good to you, I can work on this one.
PS: useLocalStorageForAccessToken might be a bad variable name, open to suggestions :)
Summary
keystatic-gh-access-tokenis stored in a non-HttpOnlycookie because the browser reads it directly when making GitHub API calls. The problem is that some enterprise firewalls (Fortinet being a common one) automatically addHttpOnlyto all cookies going outside of the network, which breaks the Keystatic admin UI since JavaScript can no longer access the token.Problem
The token is set like this:
keystatic/packages/keystatic/src/api/generic.ts
Lines 263 to 272 in 65efeac
The token is read like this:
keystatic/packages/keystatic/src/app/auth.ts
Lines 16 to 17 in 65efeac
httpOnlyis intentionally left out so the frontend can read the token and authenticate GitHub API requests. But behind certain network firewalls, that choice gets overridden, the cookie getsHttpOnlystamped onto it, and things break. There's no workaround available to the application once that happens.Solutions
Option 1:
localStoragefallback (opt-in flag)Add a config flag in keystatic.config.ts under storage if kind is github that switches token storage from a cookie to
localStorage, similar to how Keystatic Cloud auth already works (localStorage.getItem('keystatic-cloud-access-token')). Users in restricted environments could opt into this, leaving the default behavior unchanged.For Example:
{ "storage": { "kind": "github", "repo": "thinkmill/keystatic", "useLocalStorageForAccessToken": true } }Option 2: Server-side GitHub proxy (preferred)
Route GitHub API requests through Keystatic's existing API routes instead of making them directly from the browser:
The browser never touches the token, so
keystatic-gh-access-tokencan beHttpOnly(or dropped from the client entirely). This also closes off client-side token exposure as a side benefit.Contribution
If the issue looks good to you, I can work on this one.
PS:
useLocalStorageForAccessTokenmight be a bad variable name, open to suggestions :)