This guide is written by the team that builds Casdoor and describes the standards as published (listed under Sources), with notes on how Casdoor implements them as of November 1, 2026. Spotted a mistake? Tell us.
Two SSO logins can look identical to the user and behave very differently underneath. The difference is who starts the flow:
- SP-initiated: the user opens the application (the service provider), which redirects to the identity provider.
- IdP-initiated: the user starts at the identity provider — from its portal or an app tile — and the identity provider pushes a response to the application.
Most enterprise integrations need both. This article explains how each works and what to watch out for.
SP-initiated SSO
This is the standard flow and the one every protocol is designed around.
- The user opens the application, which has no session.
- The application creates a SAML AuthnRequest (or an OIDC authorization request) and redirects the browser to the identity provider.
- The identity provider authenticates the user, or reuses an existing session.
- It returns a SAML response to the application's ACS URL (or an OIDC authorization code to the callback).
- The application validates the response and starts its own session.
Why it is the default:
- The application controls the entry point, so it can carry state: the page the user wanted, a deep link, a tenant hint.
- The identity provider knows which request it is answering: the SAML response carries an
InResponseTovalue that matches the request ID. - The application decides whether to offer SSO at all, and for which tenant.
IdP-initiated SSO
Here the identity provider sends an unsolicited response: there was no AuthnRequest, so there is no InResponseTo.
- The user signs in to the identity provider's portal.
- The user clicks the application's tile.
- The identity provider POSTs a SAML response to the application's ACS URL.
- The application accepts it and starts a session.
This is convenient — it makes the identity provider a launchpad for every application — but it has consequences:
- No request context. Deep links and in-app state are lost unless the identity provider passes a
RelayStatevalue. - Replay and injection risk. An unsolicited response can be replayed or forged if the application does not validate issuer, audience, recipient and replay cache strictly.
- Not every application supports it. Many SPs reject unsolicited responses on principle.
- OIDC is less standardized here. OIDC defines the code flow; "IdP-initiated" is usually implemented as a portal link to the app's login URL, not as a standard unsolicited token.
Side by side
| SP-initiated | IdP-initiated | |
|---|---|---|
| Who starts | Application | Identity provider portal |
| Request from SP | Yes (AuthnRequest / authorization request) | No |
InResponseTo |
Present and validated | Absent |
| Deep links | Natural | Needs RelayState |
| Replay protection | Request ID match | Must rely on issuer, audience, replay cache |
| Typical use | Application login buttons, bookmarks | Employee portals, app tiles |
The security argument
IdP-initiated SSO is not insecure by design, but it removes one check: the response is not tied to a request the application just made. Everything else has to compensate:
- Validate the issuer: only accept assertions from the configured identity provider.
- Validate the audience: the assertion must name this application.
- Validate the recipient / ACS URL.
- Keep a replay cache of assertion IDs until they expire.
- Enforce short validity windows and clock skew limits.
Some security teams disable IdP-initiated SSO entirely for these reasons. Others allow it only for applications that validate strictly. The right answer depends on who operates the identity provider and what the application supports.
How Casdoor handles both
Casdoor supports both flows as a SAML identity provider.
- SP-initiated works out of the box: the application redirects to Casdoor, and Casdoor posts the response to the configured reply URL (HTTP POST) or to the assertion consumer URL in the request (redirect binding when no reply URL is set).
- IdP-initiated requires the application's SAML reply URL to be set; otherwise the login fails. Casdoor shows the launch URL on the application's SAML tab:
<casdoor-host>/login/saml/authorize/admin/<application-name>
You can pass ?RelayState=/dashboard to send the application a landing path. The response has no InResponseTo, because no request preceded it.
How to choose
- Start with SP-initiated. It is what applications expect and what protocols standardize.
- Add IdP-initiated when customers ask for portal tiles or bookmarks, and only for applications that validate assertions strictly.
- Document the difference for your support team: the error messages differ ("response not for this recipient" versus "InResponseTo mismatch"), and so do the fixes.
Related reading: set up Casdoor as a SAML IdP, SAML single logout and identity provider vs service provider.
Casdoor is open source and self-hostable, with a hosted option from $9 per month and no per-user fees.
Sources
- OASIS: SAML 2.0 technical overview
- Casdoor docs: Casdoor as a SAML identity provider
- OpenID Connect Core 1.0
Product names are trademarks of their respective owners.