Skip to content
Free lab

SSO Misconfiguration Lab

A broken SSO connection does not look broken — it is one setting out of seven that is wrong. Review a queue of decoded SAML and OIDC Service-Provider connections and call each one: hardened, or name the defect — a signature that is never validated or only checks the envelope (XML Signature Wrapping), an open redirect in the RelayState or redirect_uri, an unsolicited IdP-initiated response with no compensations, an audience that is never bound, a replayable assertion, a weak NameID, or an unpinned signing key. You are scored on catch rate against false positives — because flagging a hardened connection (a compensated IdP-initiated portal, a PKCE public client) is its own failure. Clear the review clean.

The real job. More noise, a tighter clock.

0sPro

SSO connection review

Call each SP connection: OK, or name the specific SAML / OIDC misconfiguration.

#1

wiki (SAML SP)

SAML · HTTP-POST
signatureAssertion signed; SP verifies it against the pinned IdP certificate and refuses any assertion lacking a valid signature.
redirectRelayState matched against a server-side allowlist of exact return URLs; unknown values rejected.
solicitedSP-initiated only — InResponseTo must match an outstanding AuthnRequest ID.
audienceAudience, Recipient and Destination all validated against this SP entityID and ACS URL.
lifetime5-minute NotOnOrAfter window; assertion IDs cached one-time-use to block replay.
nameIDPersistent, IdP-scoped NameID used as the stable account key.
metadataIdP signing certificate pinned; rotation is a reviewed change.

purpose: Internal wiki — SP-initiated SSO

Verdict

#2

crm (SAML SP)

SAML · HTTP-POST
signatureSP does not verify the Response or Assertion signature at all — any well-formed SAML Response is accepted.
redirectRelayState limited to a fixed set of relative paths on the SP origin.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetime4-minute window; assertion IDs cached one-time-use.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Sales CRM — production SSO

Verdict

#3

helpdesk (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectAfter login the SP redirects the browser to the RelayState value verbatim, with no allowlist or same-origin check.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetime5-minute window; one-time-use replay cache.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Support portal — production SSO

Verdict

#4

analytics (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectRelayState allowlisted to exact return URLs.
solicitedUnsolicited IdP-initiated Responses are accepted; InResponseTo is absent and not checked, and there is no compensating one-time-use or tight window.
audienceAudience and Recipient validated.
lifetime8-hour NotOnOrAfter window.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: BI dashboard — production SSO

Verdict

#5

timesheets (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetimeNotOnOrAfter is 24 hours after issue and no replay cache is kept, so a captured assertion is reusable for a day.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Time-tracking app — production SSO

Verdict

#6

expenses (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceAudienceRestriction, Recipient and Destination are not checked — any valid assertion from the IdP is accepted.
lifetime5-minute window; one-time-use replay cache.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Expense app — production SSO

Verdict

#7

billing-ui (OIDC RP)

OIDC · Auth Code + PKCE
signatureID token signature verified (RS256) against the IdP JWKS; iss and aud claims checked.
redirectredirect_uri matched by exact string against the single registered value; state validated to the originating session.
solicitedstate binds the callback to the SP-initiated request; foreign or replayed callbacks rejected.
audienceaud must equal this client_id and iss must equal the configured issuer.
lifetimeID token valid 5 min; nonce bound to the auth request and accepted exactly once.
nameIDsub claim (stable, issuer-scoped) used as the account key.
metadataJWKS fetched over HTTPS discovery and pinned by kid; rotation via signed discovery.

purpose: Billing SPA — production login

Verdict

#8

forum (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetime5-minute window; one-time-use replay cache.
nameIDNameID Format is transient (a fresh random value each login), yet the SP stores it as the permanent account identifier.
metadataSigning certificate pinned.

purpose: Community forum — production SSO

Verdict

#9

docs (SAML SP)

SAML · HTTP-POST
signatureAssertion signature is verified — against whatever signing certificate the IdP metadata currently advertises.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetime5-minute window; one-time-use replay cache.
nameIDPersistent NameID as the account key.
metadataIdP metadata (including the signing certificate) is fetched from a URL at startup over plain HTTP and trusted on first use — no pinning and no signature on the metadata.

purpose: Document store — production SSO

Verdict

#10

payroll (SAML SP)

SAML · HTTP-POST
signatureThe SAML Response envelope is signed and the signature verifies, but the Assertion itself is unsigned and the SP simply trusts the first Assertion element it finds in the document.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceAudience, Recipient and Destination validated.
lifetime5-minute window; one-time-use replay cache.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Payroll app — production SSO

Verdict

#11

grants (SAML SP)

SAML · HTTP-POST
signatureAssertion signed (RS256); the signature reference is pinned to the consumed Assertion element, and assertions lacking a valid signature are rejected.
redirectRelayState limited to a fixed set of relative paths resolved against the SP origin.
solicitedSP-initiated; InResponseTo validated against outstanding request IDs.
audienceAudience, Recipient and Destination all validated.
lifetime3-minute window; assertion IDs cached one-time-use.
nameIDPersistent NameID is the account key; email is carried only as a display attribute.
metadataSigning certificate pinned; rotation via overlapping keys in signed metadata.

purpose: Research grants portal — production SSO

Verdict

#12

portal-spa (OIDC RP)

OIDC · Auth Code + PKCE
signatureID token verified (RS256) against pinned JWKS; iss and aud checked.
redirectredirect_uri is accepted if it starts with the registered prefix https://app.example.com — so https://app.example.com.attacker.tld and https://app.example.com/../evil also pass.
solicitedstate binds the callback to the request.
audienceaud == client_id; iss == issuer.
lifetimeID token 5 min; nonce bound and one-time.
nameIDsub used as the account key.
metadataJWKS pinned by kid over signed discovery.

purpose: Customer portal SPA — production login

Verdict

#13

reports-api (OIDC RP)

OIDC · Auth Code
signatureID token verified (RS256) against pinned JWKS; iss and aud checked.
redirectExact redirect_uri match; state validated to the session.
solicitedstate binds the callback to the request.
audienceaud == client_id; iss == issuer.
lifetimeID token lifetime is a tight 5 minutes, but no nonce is bound to the request and no jti / one-time-use is enforced — a captured ID token is replayable within the window.
nameIDsub used as the account key.
metadataJWKS pinned by kid over signed discovery.

purpose: Reporting web app — production login

Verdict

#14

inventory (SAML SP)

SAML · HTTP-POST
signatureAssertion signed and verified against the pinned IdP certificate.
redirectRelayState allowlisted to exact return URLs.
solicitedSP-initiated; InResponseTo validated.
audienceRecipient is validated against the ACS URL, but AudienceRestriction is not checked.
lifetime5-minute window; one-time-use replay cache.
nameIDPersistent NameID as the account key.
metadataSigning certificate pinned.

purpose: Inventory app — production SSO

Verdict

Catch rate vs. false positives

Review each connection, then grade. So far 0/14 called, 0/11 misconfigs caught, 0 false positive(s).

How it is scored

Catch rate = misconfigured connections given the exact right finding.

A false positive (flagging a hardened connection) takes an extra hygiene bite — a review that cries wolf is as useless as one that misses a forged-assertion gap.

Amber fields are a “read twice” cue, not the answer. Harder tiers add subtler configs and deliberate traps (a hardened IdP-initiated portal, a PKCE public client, a signature pinned to the consumed assertion).

Live arena

4 active
adversary kill-chain1/4
sso-admin-dana12%
iam-eng-rao9%
sso-admin-you (you)0%
appsec-kim0%

adversary staging…

TL;DR

What this lab is

  • A free, browser-based SSO-misconfiguration lab: review a queue of ~20 decoded SAML and OIDC Service-Provider connections and call each one OK or name its specific defect.
  • Defects seeded across the queue: signature not validated / XML Signature Wrapping, open redirect in the ACS RelayState or redirect_uri, unsolicited IdP-initiated acceptance, Audience / Recipient / Destination not bound, assertion lifetime too long or no replay protection, weak NameID / identity handling, and an IdP signing key that is not pinned.
  • You are graded on catch rate weighed against false positives — flagging a correctly-hardened connection takes an extra hygiene penalty, because a review that cries wolf is as useless as one that waves through a forged-assertion gap.
  • Difficulty tiers add subtler configs and deliberate traps: a hardened IdP-initiated portal, a PKCE public client with no secret, and a signature reference pinned to the consumed assertion.
  • Pure SAML / OIDC hardening — nothing is parsed or sent anywhere, the connections are authored decoded summaries, and after grading every connection shows why the right verdict is right.

Frequently asked questions

What is the SSO Misconfiguration Lab?

A free, in-browser exercise where you review a queue of decoded SAML and OpenID Connect Service-Provider connections — how the signature is validated, how the post-login redirect (RelayState / redirect_uri) is checked, whether unsolicited IdP-initiated responses are accepted, whether Audience / Recipient / Destination are bound, the assertion lifetime and replay protection, how identity (NameID / sub / email) is keyed, and whether the IdP signing key is pinned — and give each one a verdict: OK, or a specific misconfiguration. You are scored on how many real defects you catch versus how many hardened connections you wrongly flag.

How is it graded?

Each connection has one correct verdict. Your catch rate is the share of misconfigured connections you identify with the exact right finding. False positives — flagging a correctly-hardened connection — carry an extra hygiene penalty, because a review that security engineers cannot trust is as useless as one that misses a signature-wrapping or open-redirect gap. A perfect run calls every connection exactly right, sound ones included.

What SSO security concepts does it cover?

Real SAML and OIDC hardening: verifying the assertion signature (not just the Response envelope) and binding the signed reference to the consumed element to block XML Signature Wrapping; exact-match allowlisting of the ACS RelayState and OIDC redirect_uri to prevent open redirects; requiring InResponseTo for SP-initiated flows and compensating hardened IdP-initiated use-cases; validating Audience / Recipient / Destination so a token minted for another SP is rejected; tight NotOnOrAfter windows plus one-time-use (assertion-ID / nonce / jti) replay protection; keying accounts on a stable, verified subject rather than a transient NameID or unverified email; and pinning the IdP signing certificate instead of trusting unauthenticated or auto-refreshed metadata.

Does it process real SSO traffic?

No. Nothing is uploaded, no SAML Response or OIDC token is parsed, and no cryptography runs. The connections are authored, realistic decoded configuration summaries bundled with the page, and the lab is fully deterministic. It runs entirely in your browser — no account, no install, nothing sent anywhere.

Who is it for?

IAM and platform engineers who configure SAML / OIDC connections, application-security reviewers who sign off SSO integrations, and anyone prepping for a security interview who wants to feel why an envelope-only signature, an unvalidated RelayState, an unsolicited assertion with no compensations or an unpinned signing key are findings — and why a hardened IdP-initiated portal or a PKCE public client is not.

🎉 New here? Use code WELCOME10 for 10% off any plan at checkout
SSO Misconfiguration Lab — Free Interactive SAML & OIDC Trainer · PlayCISO