8000 Persist the CSRF secret when updating a session by lisboarocha · Pull Request #6832 · hedgedoc/hedgedoc · GitHub
[go: up one dir, main page]

Skip to content

Persist the CSRF secret when updating a session - #6832

Open
lisboarocha wants to merge 1 commit into
hedgedoc:developfrom
prefitech:fix/session-store-csrf-token
Open

lisboarocha wants to merge 1 commit into
hedgedoc:developfrom
prefitech:fix/session-store-csrf-token

Conversation

@lisboarocha
Copy link
Copy Markdown
Contributor

Fixes #6831.

KnexSessionStore.set() upserts the session with onConflict(id).merge([...]), and csrfToken was missing from the merge list, so a CSRF secret generated for a session that already existed was never persisted. This adds it, with a unit test.

Change

  • backend/src/sessions/knex-session-store.ts: add FieldNameSession.csrfToken to the merge list. Every other persisted field was already there, except id, which is the conflict key, and createdAt, which is excluded on purpose.
  • backend/src/sessions/knex-session-store.spec.ts: a test asserting that the generated upsert updates csrf_token on conflict.

Verification

All on develop at f199a28.

The test fails without the change, and the generated SQL shows why:

on conflict ("id") do update set "user_id" = excluded."user_id", "pending_user_data" = excluded."pending_user_data", … "updated_at" = excluded."updated_at"

With the change, the suite passes: 6 of 6.

Built backend, before and after, same reproduction as in the issue (sqlite, one OIDC provider configured):

                                        without   with
GET    /api/private/auth/oidc/<id>        302     302
GET    /api/private/csrf/token            200     200
DELETE /api/private/auth/pending-user     403     204

Suites: backend unit tests 45 suites, 578 tests passed (577 before the change, plus the new one); backend e2e on sqlite 15 suites, 405 tests passed, the same as before the change; oxlint 0 warnings and 0 errors; oxfmt --check clean.

KnexSessionStore.set() writes a session with
insert ... onConflict(id).merge([...]), and csrfToken was missing from
the merge list. The secret was therefore written only when the session
row was first inserted: a CSRF token generated for a session that
already existed was never persisted, and the next state-changing
request to the private API failed with 403 "Missing csrf secret".

With saveUninitialized disabled, a session row can exist before any
token has been requested. GET /api/private/auth/oidc/:id is one such
path: it saves the session with pendingUser. A client that then asks
for a token and confirms the pending user gets the 403.

Add csrfToken to the merge list, and a unit test asserting that the
upsert updates csrf_token on conflict. The test fails without the
change.

Signed-off-by: Waldir Lisboa Rocha <lisboarocha@prefi.tech>
@ErikMichelson
ErikMichelson self-requested a review September 25, 2026 13:07

This branch has not been deployed

No deployments
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.

Session store drops the CSRF secret when it updates an existing session

1 participant

0