etherpad-lite/src/node
John McLear 682cb65625
security(oidc): stop shipping a hardcoded cookie key and reflecting all CORS origins (#8070)
* security(oidc): stop shipping a hardcoded OIDC cookie key and reflecting all CORS origins

The embedded OIDC provider signed its interaction/session/grant cookies
with the committed literal key `['oidc']`, so anyone with the public
source could forge valid `.sig` cookies (defeating the provider's
cookie-integrity boundary), and `clientBasedCORS` returned `true` for
every origin, reflecting arbitrary `Origin` values into
`Access-Control-Allow-Origin` on the token/userinfo endpoints.

Adds OidcProviderSecurity.ts with two unit-tested pure helpers:

- resolveOidcCookieKeys(): prefers an operator-supplied
  `settings.sso.cookieKeys` (ordered array for rotation), otherwise
  derives a secret key from the persisted session secret via a
  domain-separated SHA-256 — stable across restarts and multi-pod, never
  committed to source; falls back to an ephemeral random key.
- isOriginAllowedForOidcClient(): allows a CORS origin only when it
  matches the origin of one of the client's registered redirect URIs.

Verified end-to-end against a real oidc-provider@9.9.1 instance
(cookies.keys accepted, Client.redirectUris read correctly, attacker
origin blocked). Reported privately by meifukun.

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

* security(oidc): use DB-backed SecretRotator for cookie keys under default rotation config

Addresses Qodo review on #8070. With Etherpad's default cookie settings
(keyRotationInterval + sessionLifetime set), key rotation is enabled and
`settings.sessionKey` stays null unless SESSIONKEY.txt is provisioned, so
the previous fallback handed the OIDC provider a per-process random key —
breaking in-flight OIDC cookies on restart and across horizontally-scaled
pods on a default install.

resolveOidcCookieKeys() now takes an optional rotatedSecrets array
(priority: operator cookieKeys > rotated secrets > session-key
derivation > random) and returns it by reference so a live rotation
propagates to keygrip. OAuth2Provider.expressCreateServer() creates a
dedicated `oidcCookieSecrets` SecretRotator — the same DB-backed
mechanism the Express session cookies use — when the operator hasn't
pinned settings.sso.cookieKeys and rotation is enabled.

Adds unit tests for the rotatedSecrets priority/by-reference behavior and
an integration test proving the default config yields stable DB-backed
secrets rather than a random key.

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

* docs(settings): give sso.cookieKeys a real key in the template

The doc block sat above `sso.clients` with no `cookieKeys` property, so the admin
UI's template-comment extractor (which attaches leading comments to the next
property node) would have shown it as documentation for `clients`. An unset
OIDC_COOKIE_KEY yields [""], which resolveOidcCookieKeys() filters out, so the
derived/rotated key path is unchanged.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: SamTV12345 <40429738+samtv12345@users.noreply.github.com>
2026-07-27 20:36:27 +02:00
..
db security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection) (#8073) 2026-07-27 20:36:24 +02:00
eejs chore: migrated settings to esm6 (#7062) 2025-08-04 22:42:50 +02:00
handler security(export): strip remote images on the soffice path to match the native path (#8071) 2026-07-27 20:18:47 +02:00
hooks security(padid): reject ueberdb delimiter ':' in pad ids (copyPad/movePad injection) (#8073) 2026-07-27 20:36:24 +02:00
security security(oidc): stop shipping a hardcoded cookie key and reflecting all CORS origins (#8070) 2026-07-27 20:36:27 +02:00
types fix(admin/pads): apply filter chip server-side, before pagination (#7798) 2026-05-17 19:43:36 +01:00
updater Fix offline/air-gapped Docker boot: stop pnpm self-provisioning the pinned version (#7911) (#7918) 2026-06-09 09:31:00 +01:00
utils security(oidc): stop shipping a hardcoded cookie key and reflecting all CORS origins (#8070) 2026-07-27 20:36:27 +02:00
metrics.ts chore: added prometheus support in Etherpad 2025-08-25 20:34:29 +02:00
padaccess.ts feat: make cookie names configurable with prefix setting (#7450) 2026-04-05 01:58:29 +01:00
prom-instruments.ts feat(metrics): 3 Prometheus counters for scaling dive (#7756) (#7762) 2026-05-15 19:54:42 +01:00
prometheus.ts feat(metrics): 3 Prometheus counters for scaling dive (#7756) (#7762) 2026-05-15 19:54:42 +01:00
README.md Added typescript to etherpad 2024-02-05 21:13:02 +01:00
server.ts fix: eliminate the Windows backend silent-ELIFECYCLE flake (handler gate + Node 24.16.0) (#7866) 2026-05-28 16:12:34 +01:00
stats.ts Added typescript to etherpad 2024-02-05 21:13:02 +01:00

About the folder structure

  • db - all modules that are accessing the data structure and are communicating directly to the database
  • handler - all modules that respond directly to requests/messages of the browser
  • utils - helper modules

Module name conventions

Module file names start with a capital letter and uses camelCase

Where does it start?

server.ts is started directly