You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/architecture/interfaces.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ Related decisions: [ADR-044](../adr/ADR-044-connectrpc-service-conventions.md),
29
29
| Surface | Mount | Contract | Access boundary |
30
30
| ------- | ----- | -------- | --------------- |
31
31
| Public ConnectRPC |`/api/connect/chatto.{auth,discovery,api,admin}.v1.*`| Unary Connect, gRPC, and gRPC-Web services | Explicit per-service public or authenticated-user policy; method-level authorization remains inside operation models |
32
-
| Browser authentication |`GET /auth/browser/csrf`, `POST /auth/browser/login`, `POST /auth/browser/register/complete`, `POST /auth/browser/logout`, `POST /auth/browser/session/migrate`, `POST /auth/browser/session/renew`, `POST /auth/browser/revoke-bearer-session`| Bound CSRF-proof repair, cookie-only password/registration authentication, one-time 0.4 typed-cookie migration, logout, stable-handle session renewal, and removal of stored origin bearer authority | Every mutation requires JSON, the browser-auth mode header, and an exact same-origin request. Renewal and logout also require signed double-submit CSRF proof while a valid cookie authority exists. Migration uses the independent browser-route proof because it runs before a current cookie session exists. Logout can clear invalid session cookies with the same proof. The safe CSRF route requires a valid cookie session. These routes do not return bearer credentials. |
32
+
| Browser authentication |`GET /auth/browser/csrf`, `POST /auth/browser/login`, `POST /auth/browser/register/complete`, `POST /auth/browser/logout`, `POST /auth/browser/session/migrate`, `POST /auth/browser/session/renew`, `POST /auth/browser/revoke-bearer-session`| Bound CSRF-proof repair, cookie-only password/registration authentication, one-time 0.4 typed-cookie migration, logout, stable-handle session renewal, and removal of stored origin bearer authority | Every mutation requires JSON and an exact same-origin request. A browser-auth mode header, if present, must select cookies. Browser routes treat an absent header as cookie mode. Renewal and logout also require signed double-submit CSRF proof while a valid cookie authority exists. Migration uses the independent browser-route proof because it runs before a current cookie session exists. Logout can clear invalid session cookies with the same proof. The safe CSRF route requires a valid cookie session. These routes do not return bearer credentials. |
33
33
| Programmatic authentication |`POST /auth/login`, `POST /auth/register/complete`, `POST /auth/logout`, `POST /oauth/token`| First-party bearer issuance, stable bearer-session revocation, and OAuth code/refresh exchange | JSON is required for direct login and registration. These routes do not create, read, or clear ambient browser authentication cookies. OAuth token exchange also accepts the documented form encoding. |
34
34
| Realtime WebSocket |`GET /api/realtime`| Binary `chatto.realtime.v1.Realtime*` frames | Bearer access token in the hello frame or same-origin cookie; exact human credentials are revalidated before subscription and once per minute; bearer expiry and cookie renewal thresholds request reconnects, while OAuth-client blocks terminate matching established sessions |
35
35
| Server OIDC client metadata |`GET /oauth/client-metadata.json`| CIMD public-client identity and exact callbacks for Chatto server login | Public; mounted only when an OIDC provider uses this deployment's metadata URL as its client ID |
Copy file name to clipboardExpand all lines: docs/fdr/FDR-023-authentication-and-sessions.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,7 @@ providers, and a bootstrap path for first-boot operator setup.
29
29
- **Chatto OAuth authorization** — cross-origin Chatto clients use `/oauth/authorize` with a stable `client_id` and PKCE to obtain a short-lived authorization code, then exchange it with that same client ID at `/oauth/token` for an opaque delegated access token plus a rotating refresh credential. The bundled multi-server client opens the remote server's authorization UI in a separate browser or Electron window, so passwords, passkeys, and external-provider sign-in remain visibly hosted by that server while the main client stays mounted. The callback returns only the authorization result to the main client, which performs the code exchange and stores the credential pair and expiries. Browser clients identify themselves through their published frontend CIMD document; Chatto validates the requested callback exactly against it. Desktop uses its fixed built-in client and callback. The first authorization shows a user consent screen and remembers approval per user + client ID. OAuth access tokens can authenticate ordinary API and realtime requests, but cannot satisfy or acquire fresh-auth status for account-security operations.
30
30
-**Cross-origin transport** — Chatto permits browser HTTP and realtime transport from any syntactically valid origin without enabling credentialed CORS. Requests carrying an `Origin` that matches neither the direct request target nor the configured public origin never authenticate through ambient session cookies and must present a bearer token. Checking the direct target keeps direct hostname aliases usable; the configured origin covers TLS-terminating proxies without trusting forwarded headers. The `allowed_origins` and `oauth_redirect_origins` settings and origin-only OAuth flow were removed for 0.5; compatibility with pre-0.5 clients is not required.
31
31
- **Cookie session** — on successful same-origin authentication, the server issues the opaque handle in an HttpOnly, SameSite=Lax `chatto_auth_<slot>` cookie with a 90-day default expiry. SCS manages the session lifecycle through a Chatto JetStream store adapter. The authoritative `RUNTIME_STATE` `session.{hmac}` record contains the user ID, kind `first_party_session`, presentation `cookie`, source/request metadata, auth generation, fresh-auth metadata, and explicit expiry. Each changed revision has a per-message TTL equal to its remaining explicit lifetime. Validation checks the explicit expiry and resolves the current user from projections without rewriting the record or browser cookie. In the final quarter, the bundled frontend calls the protected browser renewal route. The core advances the same record's expiry with revision-checked optimistic concurrency, and the route writes the same opaque handle in a fresh cookie slot with the new lifetime. A late response can add an older slot, but it cannot overwrite a newer session. Chatto examines at most four distinct handles, deduplicates one handle across concurrent response slots, selects the newest valid session, ignores invalid or revoked handles, and removes obsolete slots during the next authentication change. A server-provided renewal time drives an HTTP timer, and the realtime close signal provides a second trigger. Public discovery, ordinary API responses, frontend responses, immutable assets, and WebSocket upgrades do not carry authentication `Set-Cookie` headers. The separate encrypted `chatto_session` cookie normally holds only short-lived provider, invitation, and OAuth browser-flow state. During the 0.5 upgrade, the bundled frontend can use a same-origin migration route to read the immediately previous typed `runtime_credential_id` field once. The route adds explicit expiry and physical TTL to the existing record with optimistic concurrency, writes the same handle to a new SCS cookie slot, and removes the retired field. Ordinary authentication does not read the field, and the older `user_id` plus `cookie_session_id` shape remains unsupported. Remove this bridge in 0.6. The bundled SPA also revokes old stored origin bearer authority before it removes those credentials. A separately hosted client uses the OAuth flow instead of direct authentication.
32
-
- **CSRF protection** — cookie-authenticated unsafe routes require an `X-CSRF-Token` header matching the readable `chatto_csrf` cookie, and that cookie value must be a server-signed token bound to the authenticated user session generation. Dedicated browser login, registration, migration, renewal, bearer cleanup, and logout routes also require JSON, an exact same-origin request, and `X-Chatto-Authentication-Mode: cookie`. Plain HTML forms cannot set this header. Login, registration, migration, and bearer cleanup use the origin and custom-header proof before a session exists. Renewal and logout require the signed CSRF proof while a valid cookie session exists. If every presented session is already invalid, logout uses the independent browser-route proof and clears the stale cookies. If the readable CSRF cookie expires during a longer active session, the frontend gets a new bound proof from an authenticated safe endpoint and retries the failed request once. Test endpoints, webhooks, OAuth token exchange, and ConnectRPC programmatic API traffic are exempt. Bearer-only requests are exempt because bearer credentials are not ambient browser cookies.
32
+
- **CSRF protection** — cookie-authenticated unsafe routes require an `X-CSRF-Token` header matching the readable `chatto_csrf` cookie, and that cookie value must be a server-signed token bound to the authenticated user session generation. Dedicated browser login, registration, migration, renewal, bearer cleanup, and logout routes also require JSON and an exact same-origin request. A browser-auth mode header, if present, must select cookies. Browser routes treat an absent header as cookie mode. Plain HTML forms cannot make these requests. Login, registration, migration, and bearer cleanup use the origin proof before a session exists. Renewal and logout require the signed CSRF proof while a valid cookie session exists. If every presented session is already invalid, logout uses the independent browser-route proof and clears the stale cookies. If the readable CSRF cookie expires during a longer active session, the frontend gets a new bound proof from an authenticated safe endpoint and retries the failed request once. Test endpoints, webhooks, OAuth token exchange, and ConnectRPC programmatic API traffic are exempt. Bearer-only requests are exempt because bearer credentials are not ambient browser cookies.
33
33
- **Bearer session** — direct password/registration flows and explicit external-identity account creation issue a renewable first-party bearer session by default for programmatic clients. These routes do not create or change browser authentication cookies. The bundled browser uses the dedicated cookie-only routes, which do not create or return bearer credentials. Cross-origin clients receive the delegated form through Chatto OAuth PKCE; matched external-provider browser login remains cookie-only and never places bearer credentials in its redirect URL. Each bearer session has a short-lived opaque `cht_AT…` access token, a rotating refresh credential, and a renewable session window. Remote registered clients store the pair in device-local browser storage and send only the access token as `Authorization: Bearer …` and in realtime hello frames. Normal refresh-credential use is rotation at `/oauth/token`; the bundled client also presents it to `/auth/logout` so logout can revoke the stable session. `RUNTIME_STATE` holds an immutable fixed-expiry `session.{hmac}` access record plus a mutable `renewable_session.{hmac}` authority. Each authority revision has a per-message TTL equal to its remaining explicit lifetime. The server stores neither raw credential nor the raw refresh-recovery nonce. Delegated records use kind `oauth_access_token` and remain bound to their client ID.
34
34
-**WebSocket auth** — bearer-token clients pass the access token in the realtime hello frame, where it is checked before subscription. Cookie authentication is accepted only for same-origin browser connections or non-browser requests without an `Origin` header. The server revalidates the exact human credential before subscription and once per minute. A bearer socket ends at access-token expiry with a reconnecting `authentication_required` close; the bundled client rotates once and reconnects the same event bus with its in-memory projection resume cursor. A cookie socket ends at the start of the final renewal quarter with a reconnecting `session_renewal_required` close. The bundled frontend calls the browser renewal route and then opens the replacement socket without user action.
35
35
-**API auth failures** — protected ConnectRPC methods return unauthenticated errors for missing or invalid credentials. The bundled multi-server client prefers structured error codes and keeps stable message fallbacks for compatibility with older servers.
0 commit comments