OpenConext / OpenConext/Stepup-gssp-bundle

Set `Cache-Control: no-store` on the GSSP `/saml/sso_return` page

Open Beginner friendly
#53 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

needs-refinement
Dominant language
PHP
Stars
1
Forks
0
Avg merge
17d 37m
Merged PRs (30d)
1

Description

Component: SSOReturnController

Current situation

/saml/sso_return is reached via a 302 GET (replyToServiceProvider() returns a RedirectResponse), so HTTP cache directives apply to it. Its body is an auto-submitting form carrying a signed SAML assertion. Nothing in the bundle sets cache headers, so Symfony's conservative fallback applies:

Cache-Control: no-cache, private

(ResponseHeaderBag::computeCacheControlValue())

no-cache governs reuse, not storage (RFC 9111 §5.2.2.4): the browser may store the page and is only required to revalidate before reusing it. Crucially, that revalidation requirement does not hold for history navigation. RFC 9111 §6 places history
mechanisms outside the specification's requirements — a history mechanism "can display a previous representation even if it has expired" — and browsers use that latitude. MDN states it plainly for no-cache:

The no-cache directive does not guarantee revalidation for history navigations — such
as those made using the Back button. [...] Even when bfcache is not used, the browser may
still serve the cached response without revalidating.

So on a back navigation the browser can serve this page straight from its cache. The
auto-submit JS runs again and POSTs the same assertion a second time. createSamlResponse()
has already invalidated the state by then, so the second attempt fails and the login breaks.
We see this in production, and it does not reproduce in private browsing windows.

no-store (§5.2.2.5) is the directive that actually forbids storing the response, and is what this page needs.

Change

In SSOReturnController::createSamlResponse(), before returning:

$response->headers->set('Cache-Control', 'no-store');

Defense in depth

This keeps a short-lived authentication artifact out of on-disk storage it has no reason to be in.

Impact

  • Normal path unchanged: the page is fetched from origin and auto-submits as today.
    Nothing cacheable is lost — the page is unique per login and never reusable.
  • No reverse-proxy or CDN regression: the response is already private.
  • No test breakage: nothing in tests/ or the Behat features asserts cache headers.
  • Back button onto this page now reliably shows the "no request state present" error
    instead of a silent, broken re-POST.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at SSOReturnController::createSamlResponse() and inspect the response returned after the SAML assertion is created. Set the Cache-Control header to no-store before returning, then run the existing tests under tests/ and the Behat features; done means the response prevents storage without changing the normal auto-submit flow.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
authentication, backend
Issue type
Bug
Difficulty
1/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.