From 4a12f28dce57e232921c430f76c7d8a21737605b Mon Sep 17 00:00:00 2001 From: Bernhard Windisch Date: Tue, 21 Jul 2026 09:36:24 +0200 Subject: [PATCH] docs(saml): document request correlation, replay refusal, and IdP-initiated being out of scope The P0-2 correlation fix (#160) is admin-visible in two ways this doc did not cover: SAML login is now strictly SP-initiated (an IdP-initiated, unsolicited Response is refused, because the InResponseTo correlation is what enforces replay protection), and the ACS can now redirect to new error codes. - 'What flows through': add the correlation + single-use step. - 'Out of scope': state IdP-initiated SSO is not supported, with the reason. - 'Troubleshooting': explain saml-unsolicited / saml-unknown-request / saml-replayed / saml-provider-mismatch and their usual causes. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/admin/saml-federation.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/admin/saml-federation.md b/docs/admin/saml-federation.md index 0e05c3c9..6966bb87 100644 --- a/docs/admin/saml-federation.md +++ b/docs/admin/saml-federation.md @@ -79,12 +79,13 @@ Use the IdP's normal test users — sign in with an account that already exists When a user signs in via the SAML provider, Modgud: 1. Validates the SAML Response signature against the IdP's signing certs (from the imported / fetched metadata). -2. Verifies the audience matches our SP EntityID. -3. Extracts NameID (→ Modgud `sub`) and attributes. -4. Applies the provider's **AttributeMap** to translate IdP-specific claim URIs (e.g. Microsoft's `http://schemas.xmlsoap.org/.../emailaddress`) to stable logical names (`email`). -5. Runs the **User-Update-Script** to derive `firstname`, `lastname`, `email`, `acronym` from the claims. -6. Links the external identity to an existing Modgud user (by email-domain match or auto-link rule) **or** creates a new user via JIT provisioning if enabled. -7. Signs the user into the Modgud cookie. +2. Correlates the Response to the exact login it started: the `InResponseTo` in the assertion must match an AuthnRequest Modgud actually sent for this provider, and each such request is accepted **once**. This is what stops a captured, still-valid assertion from being replayed within its lifetime. A Response that answers no request Modgud sent — including an IdP-initiated ("unsolicited") one — is refused (see [Out of scope](#out-of-scope-v1)). +3. Verifies the audience matches our SP EntityID. +4. Extracts NameID (→ Modgud `sub`) and attributes. +5. Applies the provider's **AttributeMap** to translate IdP-specific claim URIs (e.g. Microsoft's `http://schemas.xmlsoap.org/.../emailaddress`) to stable logical names (`email`). +6. Runs the **User-Update-Script** to derive `firstname`, `lastname`, `email`, `acronym` from the claims. +7. Links the external identity to an existing Modgud user (by email-domain match or auto-link rule) **or** creates a new user via JIT provisioning if enabled. +8. Signs the user into the Modgud cookie. Group / role / permission membership stays under Modgud's control — see [Permission-Modell](../concepts/permissions) for the design rationale. Group-claim → Modgud-Group translation is handled by [auto-membership scripts on Groups](./groups#auto-membership-membership-scripts). @@ -104,6 +105,7 @@ Two halves: ## Out of scope (v1) - **Single-Logout (SLO)** — logout in Modgud only clears Modgud's cookie. The customer-IdP session stays alive. SLO is on the roadmap. +- **IdP-initiated SSO** — Modgud only accepts a SAML Response that answers a login it started (SP-initiated). Starting the flow from the IdP's app dashboard (an "unsolicited" Response, with no `InResponseTo`) is refused by design — that correlation is what enforces replay protection. Always link to Modgud's login page (or the provider's SP-initiated login URL), not to an IdP-initiated tile. - **Modgud as SAML IdP** — Modgud is SP-only in v1. If you have a legacy app that speaks only SAML and you want Modgud to authenticate it, that's a future capability; today the answer is "use a SAML→OIDC bridge" or "use Modgud's OIDC". - **Encrypted assertions** — supported in the FlavorData config but off by default. Most IdPs only sign; they don't encrypt at the assertion level (TLS is enough). - **Artifact binding** — HTTP-Redirect and HTTP-POST only. Add on demand. @@ -113,4 +115,7 @@ Two halves: - **"Cannot enable a SAML provider without IdP metadata"** — set either MetadataUrl or MetadataXml in the Connection tab and save, then enable. - **IdP error: "Unable to locate metadata for ..."** — the IdP doesn't know about our SP EntityID. Re-import our SP metadata at `https:///saml//sp-metadata` into the IdP setup. - **Login redirects to error page** — check the backend log around the time of the failed login. Common causes: clock skew (assertion's NotBefore/NotOnOrAfter outside the validation window), wrong audience (SP EntityID mismatch), or signature validation failure (IdP rotated its key but Modgud's cache is stale — hit "Refresh metadata" or wait for the next scheduled refresh). +- **`error=saml-unsolicited` / `error=saml-unknown-request`** — the Response didn't answer a login Modgud started. The usual cause is an **IdP-initiated** login (started from the IdP's app tile) — not supported, see [Out of scope](#out-of-scope-v1); send users to Modgud's login page instead. `saml-unknown-request` can also mean the login took longer than the 15-minute correlation window, or the app instance that started it and the one that received the Response don't share a database. +- **`error=saml-replayed`** — the Response was already consumed. A single sign-in issues one assertion that Modgud accepts once; a second submission of the same assertion (browser back/refresh on the ACS POST, or an actual replay attempt) lands here. Start a fresh login. +- **`error=saml-provider-mismatch`** — the Response arrived at a different provider's ACS than the one that sent the AuthnRequest. Usually a mis-wired ACS URL in the IdP config — re-check the `` in the ACS URL you gave the IdP. - **User signed in but with wrong identity** — check the Linking & Policies tab. Email-based auto-linking can bind an incoming SAML identity to an existing Modgud user whose email matches. Turn off "Email-based auto-linking" if you want SAML logins to JIT-create users instead.