Skip to content

test(security): drive SAML replay protection through the real HTTP endpoints#161

Merged
windischb merged 1 commit into
developfrom
fix/security-saml-replay-e2e-test
Jul 21, 2026
Merged

test(security): drive SAML replay protection through the real HTTP endpoints#161
windischb merged 1 commit into
developfrom
fix/security-saml-replay-e2e-test

Conversation

@windischb

Copy link
Copy Markdown
Contributor

Follow-up to #160. That PR shipped the SAML request-correlation fix (P0-2) but
noted a coverage gap: the HTTP ACS path was not exercised end to end, because the
repo had no rig for minting signed SAML Responses, so the flow's wiring rested on
review rather than test. This closes that gap.

What it does

Stands up a self-hosted test IdP — ITfoxtec itself, configured with a throwaway
signing cert whose public half is published in the metadata XML the SP validates
against — and drives the real GET /saml/{slug}/login and POST /saml/{slug}/acs.

It proves the two things the store-level SamlReplayProtectionTests cannot:

  • InResponseTo round-trips. The AuthnRequest ID the flow persists at /login
    is the exact ID that must ride InResponseTo back through the ACS. This is the
    real risk in the fix — a mismatch would break every SAML login, not just
    replays. The happy-path test asserts the pending request is actually consumed,
    which can only happen if the round-trip matches.
  • Ordering. A correctly-signed Response with an unknown InResponseTo clears
    signature validation and is then refused at correlation
    (saml-unknown-request), confirming signature-first / correlate-second.

Replay of one captured, signed Response is refused with saml-replayed.

Notes

  • Same library on both ends, so the signature is real, not stubbed. The minted
    assertion is assertion-signed (AuthnResponseSignType = SignAssertion) to satisfy
    the SP's default WantAssertionsSigned floor.
  • A manual dev IdP already exists (dev/saml-idp/, simpleSAMLphp via Docker) but is
    amd64-only and not wired into CI; this in-process rig runs anywhere the suite runs
    and is a reusable base for the other SAML assertions that currently rest on review
    (signature toggles, claim mapping, XML-wrapping defenses).
  • No production code change. Integration 534/534.

🤖 Generated with Claude Code

…dpoints (P0-2)

Closes the coverage gap the store-level test called out. This one stands up a
self-hosted test IdP (ITfoxtec configured with a throwaway signing cert whose
public half is published in the metadata the SP validates against) and drives
the real GET /saml/{slug}/login and POST /saml/{slug}/acs.

It proves the two things the store-level test cannot:

- The AuthnRequest ID the flow persists is the exact ID that rides InResponseTo
  back through the ACS. This is the real risk in the fix: a mismatch would break
  every SAML login, not just replays. The happy-path test asserts the pending
  request is actually consumed, which can only happen if the round-trip matches.
- Ordering: a correctly-signed Response with an unknown InResponseTo clears
  signature validation and is then refused at correlation
  (saml-unknown-request), confirming signature-first / correlate-second.

Replay of one captured, signed Response is refused with saml-replayed.

The assertion the IdP mints is assertion-signed (AuthnResponseSignType =
SignAssertion) to satisfy the SP's default WantAssertionsSigned floor.

No production code change. Integration 534/534.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@windischb
windischb merged commit 69e2bb8 into develop Jul 21, 2026
8 checks passed
@windischb
windischb deleted the fix/security-saml-replay-e2e-test branch July 21, 2026 07:33
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.

1 participant