test(security): drive SAML replay protection through the real HTTP endpoints#161
Merged
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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}/loginandPOST /saml/{slug}/acs.It proves the two things the store-level
SamlReplayProtectionTestscannot:InResponseToround-trips. The AuthnRequest ID the flow persists at/loginis the exact ID that must ride
InResponseToback through the ACS. This is thereal 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.
InResponseToclearssignature 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
assertion is assertion-signed (
AuthnResponseSignType = SignAssertion) to satisfythe SP's default
WantAssertionsSignedfloor.dev/saml-idp/, simpleSAMLphp via Docker) but isamd64-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).
🤖 Generated with Claude Code