etherpad-lite/doc/cookies.md
John McLear 49bc33f019
feat(gdpr): HttpOnly author-token cookie (PR3 of #6701) (#7548)
* docs: PR3 GDPR anonymous identity hardening design spec

* docs: PR3 GDPR anon identity implementation plan

* feat(gdpr): ensureAuthorTokenCookie helper — HttpOnly server-set author token

* feat(gdpr): set HttpOnly author-token cookie from the pad routes

* feat(gdpr): read author token from cookie first, keep message.token fallback

* feat(gdpr): stop generating the author token client-side

* test(gdpr): server sets + reuses the HttpOnly author-token cookie

* fix+test(gdpr): parse token cookie from handshake Cookie header

socket.io handshake doesn't run cookie-parser, so socket.request.cookies
is undefined. Parse the Cookie header directly in handleClientReady so
the HttpOnly token actually resolves. Playwright spec covers HttpOnly
attribute, reload-stability, and context-isolation.

* docs(gdpr): token cookie is now HttpOnly + server-set

* fix(gdpr): close two HttpOnly token bypasses

Qodo review:
- Timeslider still ran the pre-PR3 JS-cookie path: it read
  Cookies.get('${cp}token') (which HttpOnly hides), then generated a
  fresh plaintext token and overwrote the server's HttpOnly cookie with
  it, and sent token in every socket message. Strip the token read/
  write entirely from timeslider.ts and from the outgoing message
  shape; the server reads the cookie off the socket.io handshake just
  like on /p/:pad.
- tokenTransfer re-issued the author cookie without HttpOnly, undoing
  the hardening the first time a user transferred a session. Re-set
  it as HttpOnly + Secure (on HTTPS) + SameSite=Lax. Also stop
  trusting the body-supplied token on POST: read it off req.cookies
  server-side so the client never needs JS access to the token.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 05:56:56 +01:00

18 lines
6.5 KiB
Markdown

# Cookies
Cookies used by Etherpad.
| Name | Sample value | Domain | Path | Expires/max-age | Http-only | Secure | Usage description |
|-------------------|----------------------------------|-------------|------|-----------------|-----------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| express_sid | s%3A7yCNjRmTW8ylGQ53I2IhOwYF9... | example.org | / | Session | true | true | Session ID of the [Express web framework](https://expressjs.com). When Etherpad is behind a reverse proxy, and an administrator wants to use session stickiness, he may use this cookie. If you are behind a reverse proxy, please remember to set `trustProxy: true` in `settings.json`. Set in [webaccess.js#L131](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/node/hooks/express/webaccess.js#L131). |
| language | en | example.org | / | Session | false | true | The language of the UI (e.g.: `en-GB`, `it`). Set by the pad client when the user changes **My View → Language** (currently in `src/static/js/pad.ts`, via `setMyViewLanguage()`). |
| prefs / prefsHttp | %7B%22epThemesExtTheme%22... | example.org | /p | year 3000 | false | true | Client-side preferences (e.g.: font family, chat always visible, show authorship colors, ...). Set in [pad_cookie.js#L49](https://github.com/ether/etherpad-lite/blob/01497aa399690e44393e91c19917d11d025df71b/src/static/js/pad_cookie.js#L49). `prefs` is used if Etherpad is accessed over HTTPS, `prefsHttp` if accessed over HTTP. For more info see https://github.com/ether/etherpad-lite/issues/3179. |
| token | t.tFzkihhhBf4xKEpCK3PU | example.org | / | 60 days | true | true | A random token representing the author, of the form `t.randomstring_of_length_20`. Set by the server as an `HttpOnly; SameSite=Lax` cookie on the first GET to `/p/:pad` (see `src/node/utils/ensureAuthorTokenCookie.ts`). The server reads the cookie from the socket.io handshake in `PadMessageHandler.handleClientReady` to resolve the author. Not readable from browser JavaScript. See [privacy.md](privacy.md). |
For more info, visit the related discussion at https://github.com/ether/etherpad-lite/issues/3563.
Etherpad HTTP API clients may make use (if they choose so) to send another cookie:
| Name | Sample value | Domain | Usage description |
|-----------|------------------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| sessionID | s.1c70968b333b25476a2c7bdd0e0bed17 | example.org | Sessions can be created between a group and an author. This allows an author to access more than one group. The sessionID will be set as a cookie to the client and is valid until a certain date. The session cookie can also contain multiple comma-separated sessionIDs, allowing a user to edit pads in different groups at the same time. More info - https://github.com/ether/etherpad-lite/blob/develop/doc/api/http_api.md#session |