Skip to content

handleSchnorrComplete 504s when the _interaction cookie is missing #412

Description

@melvincarvalho

Symptom

GET /idp/interaction/<uid>/schnorr-complete?accountId=<id> hangs and the gateway times out with 504. JSS log shows:

```
Schnorr login completed (accountId=b0b1707f-... uid=_T_qlTgrG3zS1...)
Promise errored, but reply.sent = true was set
err: SessionNotFound: invalid_request — interaction session id cookie not found
at #getInteraction (oidc-provider/lib/provider.js:405)
at Provider.interactionResult (provider.js:199)
at Provider.interactionFinished (provider.js:217)
at handleSchnorrComplete (src/idp/interactions.js:833)
```

The connection hangs because the handler called `reply.hijack()` before `provider.interactionFinished(...)`, so when `interactionFinished` throws, Fastify can't send the error and the socket sits open until the gateway times it out.

Reproduction

Anything that breaks `_interaction` cookie continuity between the OIDC `/auth` step and `/schnorr-complete`:

  • Start the OIDC flow in browser A, copy-paste the interaction URL into browser B (today's repro)
  • Third-party cookie blocking on a cross-site IdP/RP boundary
  • Incognito → main session
  • The same flow after a long idle (cookie expired but interaction record still findable)

Cause

`handleSchnorrComplete` (`src/idp/interactions.js:805`) does the right thing structurally — looks up the interaction by `uid`, validates the accountId — and then calls `provider.interactionFinished(request.raw, reply.raw, interaction.result, ...)`. `interactionFinished` internally re-reads the `_interaction` cookie via `#getInteraction` and explodes when it's missing, even though we already proved we have the right interaction.

Two compounding issues:

  1. Unnecessary cookie dependency. We have the interaction object in hand. The cookie check is redundant for this code path.
  2. Hijack-then-throw silently hangs. `reply.hijack()` is called BEFORE `interactionFinished`. If `interactionFinished` throws, Fastify's `Promise errored, but reply.sent = true was set` warning fires and nobody writes a response. Connection hangs → 504.

Fix sketches

A. Move the OIDC finalize into `handleSchnorrLogin`. The login handler already saves `interaction.result` and has the `_interaction` cookie in flight (it's still the same browser, same request). After saving, it can call `provider.interactionFinished(request.raw, reply.raw, result, {...})` and return its redirect target as `redirectUrl` in the JSON. Drop `schnorr-complete` entirely. This is the cleanest fix because it removes the cookie-dependency window.

B. Keep `schnorr-complete` but bypass `interactionFinished`. We have the saved `interaction.result` and can directly construct the OIDC continue URL (`interaction.returnTo`) ourselves. Redirect the browser there and oidc-provider's auth endpoint picks up the saved result without needing the cookie.

C. Defensive: don't `reply.hijack()` until AFTER `interactionFinished` resolves. At minimum this turns a 504 hang into a clean 4xx so users get a real error.

A or B fix the actual flow. C should land regardless.

Refs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions