Skip to content

Auth: support token login in core — exchange a validated JWT for an httpOnly hdb-session cookie - #1546

Merged
kriszyp merged 2 commits into
mainfrom
kris/token-login-1544
Jul 2, 2026
Merged

Auth: support token login in core — exchange a validated JWT for an httpOnly hdb-session cookie#1546
kriszyp merged 2 commits into
mainfrom
kris/token-login-1544

Conversation

@kriszyp

@kriszyp kriszyp commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Implements #1544. Studio wants direct (non-proxied) connections to instances for SSE/streaming (studio#1398) — native EventSource can't send an Authorization header, so cookie auth is the fit. This gives core the missing bridge: turn a validated JWT into an httpOnly hdb-session cookie.

  • request.login() / the login operation now accept a token alongside username/password. When given, it's validated and the resulting user is written into the session (existing request.session.update() — no new cookie machinery).
  • create_authentication_tokens / createTokens() gains purpose: 'login': mints a single short-lived (1m default, override via expires_in) token instead of an operation+refresh pair, and skips the refresh-token DB write / user-change broadcast that pair triggers.

Design decision taken

The issue flagged one open call: reuse a full operation token for the exchange, vs. mint a purpose-scoped token rejected by the Bearer-API path. I went with the recommended purpose-scoped option — the login token is signed with subject: 'login' instead of 'operation', so validateOperationToken's jwt.verify(..., { subject: 'operation' }) rejects it on a sub mismatch with no extra code, and validateLoginToken is the only thing that accepts it. This means the exchange ticket can't be replayed as a general API credential even if it leaked (e.g. in a log line) — confirmed in the new tests below (log lines literally read jwt subject invalid).

CSRF posture (design point 3) is unchanged: the cookie is still origin-prefixed per the existing mechanism, regardless of how the session was established.

Testing

  • unitTests/security/tokenAuthentication.test.js — 8 new cases: purpose: 'login' mints a login-only token with no refresh-token side effects; validateLoginToken happy/inactive-user/non-existent-user/bad/expired paths; and the two cross-subject rejection cases (operation token rejected by validateLoginToken, login token rejected by validateOperationToken).
  • integrationTests/apiTests/authentication.test.mjs — 2 new end-to-end cases: mint a login token → login op → session cookie set; and a login token sent as Authorization: Bearer → 401.
  • Ran locally: tsc --noEmit clean, full build clean, both touched unit-test files green (39/39, 31/31), both touched integration suites green (15/15, 9/9).

🤖 Generated with Claude Code

Extends the `login` operation / request.login() to accept a `token` in
addition to username/password, and extends create_authentication_tokens
with purpose: 'login' to mint it. The minted token carries subject:'login'
instead of 'operation', so validateOperationToken's Bearer-API path
rejects it automatically — it can't double as a standing API credential,
only as a one-shot ticket the login endpoint trades for a session cookie.

Closes #1544.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@claude

claude Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Reviewed; no blockers found.

Comment thread security/tokenAuthentication.ts
Per Dawson's review — the login-scoped token comes back in the same
operation_token field instead of a new login_token field, so the
create_authentication_tokens response shape doesn't change based on
purpose.
@kriszyp
kriszyp merged commit 4c45406 into main Jul 2, 2026
49 checks passed
@kriszyp
kriszyp deleted the kris/token-login-1544 branch July 2, 2026 15:29
@dawsontoth

Copy link
Copy Markdown
Contributor

Saved HarperFast/studio#1408 so we can integrate this into Studio when supported.

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.

2 participants