Skip to content

IdP errors with SessionNotFound on stale session cookies instead of expiring + restarting the interaction #526

@melvincarvalho

Description

@melvincarvalho

Symptom

After re-provisioning a pod (or otherwise rotating cookie-signing keys), a browser that still holds an old _session / _interaction cookie for the IdP origin fails sign-in at the consent / "Allow access" step. provider.interactionFinished(...) throws and the request 400s:

SessionNotFound: invalid_request — "session not found"
  at #getInteraction (oidc-provider/lib/provider.js)
  at Provider.interactionResult / interactionFinished
"Promise errored, but reply.sent = true was set"

Login + the interaction pages all succeed; only the final confirm (interactionFinished) dies. The user is stuck until they manually clear site data for the IdP origin. Incognito works (no stale cookie), which pinpoints it.

Repro

  1. Sign in to a pod, leave the browser cookies in place.
  2. Re-provision / reinstall so the persisted cookie keys change (or the stored session/interaction is gone).
  3. Sign in again from the same (non-incognito) browser → reaches consent → "Allow access" → SessionNotFound.

(Surfaced repeatedly during the Android app dev cycle — js-pod/android — across reinstalls; a fresh user wouldn't hit it, but a reinstall does.)

Why it matters

A stale cookie shouldn't be a dead end. The user has no in-app way to recover; they have to know to clear site data, which a normal user won't.

Existing precedent (the fix is small)

JSS already handles the analogous deleted-account case and already has the helpers:

  • src/idp/provider.js:268,407,420 — guard that, when a session/grant is missing due to stale cookies, calls expireSessionCookiesKoa(ctx) and redirects back to retry.
  • src/idp/cookies.jsexpireSessionCookies(reply, request) and expireSessionCookiesKoa(ctx).
  • src/idp/interactions.js:221-224 — already wraps one interactionFinished in try/catch with a fallback ("interactionFinished failed, using fallback").

Proposed fix

In the interaction handlers that call provider.interactionFinished(...) (src/idp/interactions.js — the /confirm path via src/idp/index.js:408, plus the other call sites at lines ~166, 289, 606, 644, 823), catch SessionNotFound (err.name === 'SessionNotFound') and, instead of erroring, expire the session/interaction cookies and 303 back to the authorization URL to restart the interaction cleanly — mirroring the existing deleted-account guard. Net effect: a stale cookie self-heals into a fresh login instead of a 400.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions