Skip to content

fix(serve): bootstrap session tokens for instance-token-authenticated front-ends - #116

Merged
jkyberneees merged 1 commit into
mainfrom
fix/session-token-bootstrap
Jul 26, 2026
Merged

fix(serve): bootstrap session tokens for instance-token-authenticated front-ends#116
jkyberneees merged 1 commit into
mainfrom
fix/session-token-bootstrap

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

fix(serve): bootstrap session tokens for instance-token-authenticated front-ends

Sessions created by one front-end (e.g. bodek, or the WebUI in another
browser profile) could not be loaded by another: every new session gets a
256-bit AuthToken that only reaches the creating client, and the token
bootstrap only covered legacy sessions with no token at all - so
GET /api/sessions/ returned 401 and the WebUI showed "Failed to load
session".

The GET handler now also bootstraps the session token when the caller
proves knowledge of the per-instance CSRF token by presenting it in the
X-Odek-Ws-Token header (constant-time compared). Header presentation is a
knowledge proof a cross-origin page cannot forge: a DNS-rebinding page
holds only the ambient SameSite=Strict cookie and can neither read the
token value nor set the custom header (CORS preflight, which odek does
not answer). Cookie-only callers therefore still get 401 on
token-carrying sessions, while the operator's legitimate front-ends -
which always send the header via apiHeaders - can load each other's
sessions. The rebinding damage boundary is unchanged; DELETE/POST
(rename) flows inherit the fix through the client's existing
ensureSessionToken GET bootstrap.

Regression tests cover: bootstrap with a valid instance header (200 +
X-Session-Token), no bootstrap with a wrong instance header (401), and no
bootstrap for cookie-only callers (401). Docs updated (SECURITY.md
section 24, AGENTS.md).

… front-ends

Sessions created by one front-end (e.g. bodek, or the WebUI in another
browser profile) could not be loaded by another: every new session gets a
256-bit AuthToken that only reaches the creating client, and the token
bootstrap only covered legacy sessions with no token at all - so
GET /api/sessions/<id> returned 401 and the WebUI showed "Failed to load
session".

The GET handler now also bootstraps the session token when the caller
proves knowledge of the per-instance CSRF token by presenting it in the
X-Odek-Ws-Token header (constant-time compared). Header presentation is a
knowledge proof a cross-origin page cannot forge: a DNS-rebinding page
holds only the ambient SameSite=Strict cookie and can neither read the
token value nor set the custom header (CORS preflight, which odek does
not answer). Cookie-only callers therefore still get 401 on
token-carrying sessions, while the operator's legitimate front-ends -
which always send the header via apiHeaders - can load each other's
sessions. The rebinding damage boundary is unchanged; DELETE/POST
(rename) flows inherit the fix through the client's existing
ensureSessionToken GET bootstrap.

Regression tests cover: bootstrap with a valid instance header (200 +
X-Session-Token), no bootstrap with a wrong instance header (401), and no
bootstrap for cookie-only callers (401). Docs updated (SECURITY.md
section 24, AGENTS.md).
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 26, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
odek c1b72ac Commit Preview URL

Branch Preview URL
Jul 26 2026, 04:45 PM

@jkyberneees
jkyberneees merged commit b748e50 into main Jul 26, 2026
9 checks passed
@jkyberneees
jkyberneees deleted the fix/session-token-bootstrap branch July 26, 2026 18:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant