Skip to content

Auth: verifiable end-user identity (JWT / OIDC)#84

Merged
jwicks31 merged 1 commit into
mainfrom
claude/jwt-identity
Jun 13, 2026
Merged

Auth: verifiable end-user identity (JWT / OIDC)#84
jwicks31 merged 1 commit into
mainfrom
claude/jwt-identity

Conversation

@jwicks31

Copy link
Copy Markdown
Owner

What

Optional verifiable end-user identity (JWT / OIDC). When configured, a caller's token is verified into a trusted req.user; the zero-config default is unchanged (identity is off until you set it). The last roadmap item for today.

How

  • Configure AUTH_JWT_SECRET (HS256) or AUTH_JWKS_URL + AUTH_JWT_ISSUER / AUTH_JWT_AUDIENCE (OIDC, RS/ES). Verification uses jose (not hand-rolled crypto); a bad signature / wrong iss/aud / expired / sub-less token is a clean 401.
  • The token is read from Authorization: Bearer <jwt> (only when JWT-shaped, so it never clashes with an api/project key), the X-User-Token header (coexists with a project key), or ?token= (websockets can't set headers). The auth hook then:
    • echoes it at GET /v1/me (always reachable),
    • uses sub as the trusted realtime presence identity (overrides client ?as=),
    • on an API_KEY-locked instance, accepts a valid token in place of the key — so end-users authenticate with their own IdP token instead of a shared secret.
    • Multi-tenant project keys still scope the request; identity layers on top.
  • SDK: Zero(base, { token }) sends X-User-Token + ?token=; client.me().

Deferred (next step): row-level per-user authorization (own-your-rows ACL) built on this verified identity — kept out to keep this surface small and stable.

Verification

  • SQLite 74/74 including new identity tests: HS256 auth + /v1/me echo + locked-mode authorization + invalid-token 401; OIDC RS256 verified against a live JWKS endpoint with iss/aud enforcement; and a JWT sub used as the trusted presence identity over a spoofed ?as=.
  • Postgres 53/53 (the modified auth hook runs for every request), Redis suite green.
  • Docs: README (identity section + config), /docs, .env.example, roadmap.

https://claude.ai/code/session_018efxvWw3MRjdtvE5xgBqya


Generated by Claude Code

Optionally verify a caller's token into a trusted req.user, without touching the
zero-config default (identity is off until configured).

- Configure AUTH_JWT_SECRET (HS256) or AUTH_JWKS_URL + AUTH_JWT_ISSUER/AUDIENCE
  (OIDC, RS/ES). Verification uses jose (not hand-rolled); an invalid/expired/
  wrong-iss-or-aud token, or one missing `sub`, is a clean 401.
- A token is read from Authorization: Bearer <jwt> (only when JWT-shaped, so it
  never clashes with an api/project key), the X-User-Token header, or ?token=
  (websockets can't set headers). The auth hook sets req.user and:
    * echoes it at GET /v1/me (always reachable),
    * uses sub as the trusted realtime presence identity (overrides ?as=),
    * on an API_KEY-locked instance, accepts a valid token in place of the key
      (end-users auth with their own IdP token instead of a shared secret).
  Multi-tenant project keys still scope the request; identity layers on top.
- SDK: Zero(base, { token }) sends X-User-Token + ?token=; client.me().
- Row-level per-user authorization is intentionally deferred (next step) to keep
  this surface small and stable.

Verification: 74/74 SQLite (+ identity tests: HS256 auth/echo/locked-mode/invalid,
OIDC RS256 via a JWKS endpoint with iss/aud enforcement, and trusted presence
overriding ?as=), 53/53 Postgres 16, Redis suite green. Docs updated
(.env.example, README, /docs, roadmap).
@jwicks31 jwicks31 merged commit 27bd1a7 into main Jun 13, 2026
2 checks passed
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