Introduction
The moment your internal systems exceed ten, and the moment SaaS adoption gets serious, every organization hits the same question: "How many passwords are users supposed to remember?" Single Sign-On (SSO) is the industry-standard answer to that question, and the protocols that implement the answer are SAML 2.0, OAuth 2.0, and OpenID Connect (OIDC).
There are clear reasons to revisit this topic in 2026.
1. **OAuth 2.1 becoming the de facto standard** — It is still an IETF draft (draft-ietf-oauth-v2-1), but mandatory PKCE and the removal of Implicit/ROPC have already hardened into industry best practice. It consolidates RFC 6749, RFC 7636, and RFC 9700 (the OAuth 2.0 Security BCP) into one document.
2. **Passkeys becoming the default** — As WebAuthn/FIDO2-based passkeys take over as the primary authentication factor, it has become more important to separate "how an SSO protocol conveys an authentication result" from "how the user actually authenticates at the IdP". Keycloak 26.6 integrated passkeys into the login form with conditional/modal UI.
3. **AI agent identity** — With the explosion of non-human identities, the OAuth family of protocols that deals with delegation has grown in importance. The experimental OAuth Client ID Metadata Document (CIMD) support in Keycloak 26.6 targets MCP (Model Context Protocol) authorization server scenarios.
This article walks through how SSO works, the history and role differences of the three protocols, and the decision criteria for "what to use when" — all in one place.
How SSO Works — Centralizing Trust
The essence of SSO can be summarized in one sentence: **strip authentication out of each application, delegate it to a central trusted authority, and have applications verify only the "certificate of proof" issued by that authority.**
The Cast: IdP, SP, RP
| Term | Full name | Camp | Role |
| --- | --- | --- | --- |
| IdP | Identity Provider | SAML/common | Actually authenticates users and issues proof (assertion/token) |
| SP | Service Provider | SAML | The application that trusts the IdP proof and serves the user |
| RP | Relying Party | OIDC | The OIDC term for the SP — the party relying on the IdP (OP) |
| OP | OpenID Provider | OIDC | The OIDC term for the IdP |
| AS | Authorization Server | OAuth | The authorization server that issues access tokens |
| RS | Resource Server | OAuth | The server that validates access tokens and serves APIs |
The vocabulary differs per camp, but the structure is identical: the "authenticating side" (IdP/OP/AS) and the "relying side" (SP/RP/RS) establish a trust relationship in advance, and the user only has to log in once at the authenticating side.
The Generalized SSO Flow
[User browser] [SP / RP application] [IdP / OP]
| | |
|--- 1. Access app --------->| |
| | |
|<-- 2. "Auth required, go to IdP" (redirect) --| |
| | |
|--- 3. Auth request (AuthnRequest / authorize) ------>|
| | |
|<-- 4. Login UI (password, passkey, MFA...) ----------|
|--- 5. Submit credentials --------------------------->|
| | |
|<-- 6. Issue proof + redirect back to app ------------|
| (SAML Response / authorization code) |
| | |
|--- 7. Deliver proof ------>| |
| |-- 8. Verify (signature/ |
| | code exchange) |
|<-- 9. App session created, service provided --| |
| | |
On the next app: the IdP already holds an SSO session,
so steps 4-5 (login UI) are skipped and step 6 happens
immediately --> this is SSO
The last lines are the key. **Because the IdP maintains an SSO session (usually a cookie) on its own domain**, from the second application onward the proof is issued instantly without a login screen. From the perspective of the user, "I logged in once and every app just opens."
How Trust Is Established
For SSO to work, the SP and IdP must exchange the following ahead of time.
- **SAML**: metadata XML exchange — entityID, the X.509 certificate for signature verification, endpoint URLs
- **OIDC**: client registration — client_id, client_secret (or asymmetric keys), a redirect URI allowlist. IdP-side information is auto-discovered via the Discovery document
If this trust configuration is broken (expired certificate, redirect URI mismatch, etc.), SSO does not work. The majority of incidents you meet in operations originate here.
Sessions vs Tokens — Where Does State Live?
To understand SSO, you first need to distinguish the two ways of maintaining "logged-in state".
Session-based (stateful)
[Browser] --(Cookie: JSESSIONID=abc123)--> [Server]
|
v
[Session store (memory/Redis)]
abc123 -> userId=42, roles=[admin]
- The server keeps state in a session store, and only an opaque session ID is handed to the browser as a cookie.
- Pros: the server can invalidate a session instantly (forced logout); no sensitive data in the cookie.
- Cons: horizontal scaling requires a shared session store; hard to cross domain boundaries (cookies are bound to a domain).
Token-based (stateless)
[Client] --(Authorization: Bearer eyJhbGciOi...)--> [API server]
|
v
Verifies the signature only
(no store lookup needed)
- A signed, self-contained token (usually a JWT) carries user info and permissions, and the client carries it around.
- Pros: stateless servers, freely crosses domain/service boundaries, fits microservices.
- Cons: an issued token is hard to revoke instantly before expiry — mitigated with short lifetimes plus refresh tokens.
SSO Uses Both
A common misconception is that "tokens replaced sessions". In real SSO architectures, **the two coexist**.
| Location | State mechanism | Role |
| --- | --- | --- |
| IdP domain | Session (cookie) | The SSO session — the basis for skipping re-login |
| Web app (server-rendered) | Its own app session (cookie) | A local session established after verifying the IdP proof |
| SPA/mobile to API | Token (Bearer) | API call authorization |
The SSO session at the IdP is a cookie, what the app receives is a token, and a traditional web app creates its own session again after token verification. Managing these three distinct lifetimes is the heart of SSO operations (and the reason logout is hard).
The History of the Three Protocols — Why Do Three Exist?
2002 2005 2006~2010 2012 2014 2025~
| | | | | |
SAML 1.0 SAML 2.0 OpenID 1/2 OAuth 2.0 OIDC 1.0 OAuth 2.1
(OASIS) (OASIS, OAuth 1.0a (RFC 6749) (auth layer (draft,
enterprise (web 2.0 API (authorization on top of PKCE required,
web SSO) delegation) framework) OAuth 2.0) Implicit removed)
SAML 2.0 (2005) — The Original Enterprise Web SSO
SAML (Security Assertion Markup Language) is an XML-based protocol standardized by OASIS. The era matters: in 2005 there were no smartphones and no SPAs, and what enterprises cared about was "let employees log in once across multiple web applications". So SAML was designed around browser redirects and HTML form POSTs, with XML Signature guaranteeing integrity. It remains the most widely used option for B2B SSO integrations with enterprise SaaS such as Workday, Salesforce, and AWS IAM Identity Center.
OAuth 2.0 (2012) — An Authorization Framework
OAuth started from an entirely different problem: "I want a third-party app to read my Google contacts, but I do not want to give that app my Google password." In other words, the goal is **delegated authorization without password sharing**. OAuth 2.0, standardized as RFC 6749, is a framework that issues a limited-permission key called an access token — and **it does not define a standard way of telling who the user is.** That is the single most misunderstood point.
OIDC (2014) — An Authentication Layer on Top of OAuth 2.0
When demand for "login with OAuth" exploded, every company invented its own non-standard variant (Facebook Connect and friends). OpenID Connect 1.0 cleaned up that chaos: on top of the OAuth 2.0 flow it added the **ID Token (a JWT)** as the authentication artifact, plus the UserInfo endpoint, Discovery, and dynamic client registration. One-line summary: **OIDC = OAuth 2.0 + standardized authentication + JWT + automatic metadata discovery.**
OAuth 2.1 (in progress) — Consolidating 14 Years of Security Lessons
OAuth 2.1 is not about new features; it is a **consolidation**. It merges RFC 6749 + RFC 7636 (PKCE) + RFC 9700 (Security BCP) and removes dangerous patterns.
- PKCE mandatory for all clients (including confidential clients)
- Implicit Grant (response_type=token) deleted
- Resource Owner Password Credentials (ROPC) grant deleted
- Bearer tokens in URL query strings prohibited
- Refresh tokens must be sender-constrained or rotated
If you are designing a system in 2026, the right answer is "use OAuth 2.0 but follow the 2.1 constraints as written".
Authentication vs Authorization — The Most Important Distinction
| Question | Concept | Protocol in charge |
| --- | --- | --- |
| Who are you? | Authentication (AuthN) | SAML, OIDC |
| What are you allowed to do? | Authorization (AuthZ) | OAuth 2.0 |
By analogy:
- **OIDC/SAML** = issuing an ID card. "This person is Youngju Kim, and our IdP vouches that he authenticated with a passkey five minutes ago."
- **OAuth 2.0** = issuing a hotel key card. "This card opens room 305 and the gym. The card does not tell you who is holding it."
This is where a notorious anti-pattern comes from: **using an access token as proof of authentication**. An access token only proves "permission to call an API"; it carries no guarantee that "the person sending this request is the owner of the token". Attacks really happened where an access token issued to one app was stolen and replayed for login elsewhere, and the OIDC ID Token (a JWT whose aud claim pins the audience) is precisely the fix. Login decisions must be made with the ID Token.
The Actual Flows of the Three Protocols
SAML 2.0 — SP-initiated Web SSO
[Browser] [SP: app.example.com] [IdP: idp.corp.com]
| | |
|--- GET /dashboard -------->| |
|<-- 302 Redirect ----------- (SAMLRequest=base64+deflate)|
| | |
|--- GET /sso?SAMLRequest=...&RelayState=... ------------>|
|<-- Login screen (skipped if SSO session exists) --------|
|--- Submit credentials ---------------------------------->|
| | |
|<-- 200 + auto-submitting HTML form (SAMLResponse) ------|
|--- POST /acs (SAMLResponse, RelayState) -->| |
| |-- Verify XML signature, |
| | parse Assertion, check |
|<-- Set-Cookie: app session-| conditions (time, audience)
Characteristics: everything is front-channel via the browser (POST binding), the artifact is an XML Assertion, and it can work even without direct SP-to-IdP connectivity.
OIDC — Authorization Code Flow + PKCE
[Browser/app] [RP: app.example.com] [OP: idp.corp.com]
| | |
|--- GET /login ------------>| |
| |-- generate code_verifier, |
| | compute code_challenge |
|<-- 302 /authorize?response_type=code&client_id=... |
| &scope=openid+profile&state=...&nonce=... |
| &code_challenge=...&code_challenge_method=S256 |
| | |
|--- GET /authorize --------------------------------------->
|<-- Login screen (skipped if SSO session exists) ---------|
|--- Authentication done ----------------------------------->
|<-- 302 redirect_uri?code=AUTH_CODE&state=... ------------|
|--- GET /callback?code=... ->| |
| |--- POST /token ----------->|
| | (code + code_verifier |
| | + client auth) |
| |<-- id_token, access_token, |
| | refresh_token ----------|
| |-- verify id_token signature|
|<-- Login complete ---------| and claims |
Characteristics: tokens travel over the back channel (direct server-to-server TLS), the artifact is a JWT, PKCE defends against code interception, and Discovery automates configuration.
OAuth 2.0 Alone — Third-party API Delegation
The flow is the same as OIDC, but scope contains no openid and no id_token is issued. The only artifact is an access token, and its purpose is "calling resource server APIs", not "identifying the user".
Comparison Table — At a Glance
| Aspect | SAML 2.0 | OAuth 2.0 | OIDC 1.0 |
| --- | --- | --- | --- |
| Standardized by | OASIS, 2005 | IETF RFC 6749, 2012 | OpenID Foundation, 2014 |
| Essence | Authentication + attribute transfer | Delegated authorization framework | Authentication layer on OAuth 2.0 |
| Data format | XML (Assertion) | Undefined (usually JWT/opaque) | JWT (ID Token) + JSON |
| Integrity | XML Signature | TLS + token signing (implementation-defined) | JWS signature (keys via JWKS) |
| Channel | Mostly front-channel (Redirect/POST) | Back-channel centric | Back-channel centric |
| Metadata | Manual metadata XML exchange | None (RFC 8414 fills the gap) | Discovery document, automatic |
| Mobile/SPA fit | Poor | Good (PKCE) | Good (PKCE) |
| API authorization | Unsuitable | Its main job | The access token part is plain OAuth |
| Logout standard | Single Logout (SLO) | None | RP-Initiated/Back-Channel Logout |
| Main habitat | Enterprise B2B SSO | API delegation, service-to-service | Consumer/workforce login, modern apps |
Protocol Selection Decision Tree
START
|
+-- Q1. Do you need user login (authentication)?
| |
| +-- No (pure API delegation, service-to-service calls)
| | --> OAuth 2.0 (Client Credentials for machine-to-machine,
| | follow OAuth 2.1 constraints, apply RFC 9700)
| |
| +-- Yes
| |
| +-- Q2. Are you integrating your own app,
| | or an external customer IdP?
| |
| +-- B2B integration with an external customer IdP
| | |
| | +-- Customer supports OIDC? --> OIDC (preferred)
| | +-- Customer supports SAML only? --> SAML 2.0
| | (enterprise reality: many shops are still SAML-only)
| |
| +-- Your own app (own IdP / social login)
| |
| +-- Web, SPA, mobile, or desktop — any of them
| --> OIDC Authorization Code + PKCE
| (the 2026 default for new builds)
|
+-- Q3. Do you also need API calls after login?
--> Log in with OIDC + use the access token obtained in the
same flow for OAuth 2.0 resource access
(they are complements, not alternatives)
In summary:
- **OIDC is the default for new builds.** Practically the only reason to adopt SAML anew is "the other side supports only SAML".
- **OAuth 2.0 alone** is for pure authorization without login (service accounts, API delegation).
- **SAML** is not legacy — it remains first-string as "the lingua franca of enterprise B2B". New feature investment, however, happens on the OIDC side.
Enterprise vs B2C — Selection Criteria
| Criterion | Enterprise (workforce/B2B) | B2C (consumer) |
| --- | --- | --- |
| Primary protocol | SAML or OIDC (match the peer IdP) | OIDC |
| IdP | Corporate IdP (Entra ID, Okta, Keycloak, etc.) | Own OP + social login |
| User lifecycle | HR integration, SCIM (RFC 7644) provisioning | Self sign-up, email/phone verification |
| Auth factors | Corporate policy (forced MFA, device trust) | Passkeys first, social delegation |
| Session policy | Short, strict idle timeout | Long, friction minimized |
| Logout | SLO frequently required | Single-app logout often suffices |
| Compliance | SOC 2, ISO 27001, industry regulation | Privacy laws, GDPR |
A part frequently forgotten in enterprise SSO is **provisioning**. SSO only solves "login"; account creation, entitlement assignment, and deactivating leavers are separate problems, and SCIM is the standard there. Most incidents of "a former employee can still log in to a SaaS" stem not from SSO but from missing provisioning.
The 2026 Perspective — OAuth 2.1, Passkeys, and Beyond
Apply the OAuth 2.1 Constraints Now
There is no need to wait for OAuth 2.1 to become an RFC. Things to do today:
Checklist (OAuth 2.1 alignment)
[ ] PKCE (S256) for all clients — including confidential clients
[ ] Migrate SPAs still on Implicit Flow to Code + PKCE
[ ] Eradicate ROPC (password grant) — no exceptions, not even test code
[ ] Remove any code passing access tokens in URL query strings
[ ] Apply rotation to refresh tokens of public clients
[ ] Register redirect_uri with exact match only
How Passkeys Relate to SSO
Passkeys are not a competitor to SSO protocols — they are **the primary authentication factor inside the IdP**. The structure layers like this:
[Apps] --(OIDC/SAML: convey auth result)--> [IdP] --(WebAuthn: actual user auth)--> [passkey]
When the IdP adopts passkeys, every connected app gains phishing-resistant authentication without a single line of code changed. That is the real value of an SSO architecture. The login-form passkeys integration (conditional UI) in Keycloak 26.6 is a good example.
Other Currents Worth Watching
- **FAPI 2.0**: the financial-grade security profile is Final, and Keycloak 26.6 supports the FAPI 2.0 Security Profile and Message Signing. If you operate in a high-security domain, consider adopting the FAPI 2.0 profile.
- **AI agents and delegation**: an agent calling APIs on behalf of a user is exactly the OAuth delegation model. Specs such as Token Exchange (RFC 8693) and CIMD are evolving rapidly in this space.
- **Verifiable Credentials**: decentralized identity with an issuer-holder-verifier model is discussed as a long-term alternative to SSO, but as of 2026 the enterprise mainstream remains OIDC/SAML.
Hands-on Example — Registering OIDC and SAML Clients Side by Side on One IdP
The fastest way to internalize the concepts is to register clients for both protocols side by side on the same IdP (here, Keycloak 26.6).
First the OIDC side. For a Spring Boot app, specifying just the issuer in application.yml lets Discovery resolve every other endpoint.
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: web-dashboard
client-secret: CHANGE_ME
authorization-grant-type: authorization_code
scope: openid,profile,email
redirect-uri: "https://app.example.com/login/oauth2/code/keycloak"
provider:
keycloak:
issuer-uri: https://idp.corp.com/realms/prod
Next, create both clients with the Keycloak admin CLI (kcadm).
OIDC client (new web app)
/opt/keycloak/bin/kcadm.sh create clients -r prod \
-s clientId=web-dashboard \
-s protocol=openid-connect \
-s 'redirectUris=["https://app.example.com/login/oauth2/code/keycloak"]' \
-s publicClient=false \
-s standardFlowEnabled=true \
-s 'attributes={"pkce.code.challenge.method":"S256"}'
SAML client (legacy B2B SaaS)
/opt/keycloak/bin/kcadm.sh create clients -r prod \
-s 'clientId=https://legacy.example.com/saml/metadata' \
-s protocol=saml \
-s 'redirectUris=["https://legacy.example.com/saml/acs"]' \
-s 'attributes={"saml.signature.algorithm":"RSA_SHA256","saml_assertion_consumer_url_post":"https://legacy.example.com/saml/acs"}'
Once both apps are wired up, something interesting happens. **Same user, same IdP SSO session** — yet one app receives a JWT (the ID Token) and the other receives an XML Assertion. If the user logs in to the OIDC app first and then visits the SAML app, an Assertion is issued instantly with no login screen. It is an experiment that lets you feel firsthand that the IdP acts as a **protocol translation hub**, and that the SSO session lives one layer below the protocols.
Operational and Security Best Practices
1. **Keep token lifetimes short; renew with refresh tokens** — access tokens 5 to 15 minutes, refresh tokens with rotation. Use the ID Token only for verification right after login; do not store it.
2. **Always use state and nonce** — state defends against CSRF; nonce defends against ID Token replay. Verify, do not assume, that your library does this.
3. **Synchronize clocks** — both SAML Assertions and JWTs carry time conditions. A single server with drifted NTP creates intermittent login failures. Configure a clock skew allowance of 60 to 120 seconds during validation.
4. **Manage key/certificate lifecycles** — manage SAML certificate expiry and OIDC signing key rotation with calendars and automation. Set the JWKS cache TTL appropriately (minutes to hours) so key rotation is zero-downtime.
5. **Design logout from day one** — write down as a requirement "how far should logout reach" (app session only? the IdP SSO session? every app?). It is the hardest feature to bolt on later.
6. **IdP redundancy and incident response** — if the IdP dies, login dies for every app. Operate the IdP at your highest availability tier and use features such as the zero-downtime rolling patch updates in Keycloak 26.6.
Anti-patterns You Will Meet
| Anti-pattern | What is wrong | Fix |
| --- | --- | --- |
| Login decisions from an access token | No audience check; token substitution attacks | Decide login with the ID Token |
| New use of Implicit Flow | Tokens exposed in URL fragment | Code + PKCE |
| ROPC for first-party login | Trains users for phishing; blocks MFA/passkeys | System browser + Code + PKCE |
| JWT in localStorage | One XSS leaks all tokens | httpOnly cookie session or BFF pattern |
| Skipping/partial SAML signature checks | Assertion forgery, Signature Wrapping | Vetted library + sign both Response and Assertion |
| Wildcard redirect_uri registration | Code theft via open redirect | Exact match only |
| Refresh tokens that live forever | Indefinite session if stolen | Rotation + reuse detection |
| SSO without SCIM | Leaver accounts linger | SCIM provisioning/deprovisioning |
Closing
Compressing the relationship of the three protocols one last time:
- **OAuth 2.0** is "how to delegate permissions". It is not an authentication protocol.
- **OIDC** layers a standard "who are you" on top of OAuth 2.0 and is the default for new builds in 2026.
- **SAML 2.0** remains on active duty as the lingua franca of enterprise B2B SSO, but compatibility is virtually the only reason to choose it anew.
- **OAuth 2.1** is not a new protocol — it is "the codification of security hygiene that has become common sense". Start following it today.
The next article digs into the internals of SAML 2.0 (Assertions, Bindings, Metadata), and the one after that into the internals of OIDC (Code Flow, Discovery, token validation) at the code level.
References
- [RFC 6749 — The OAuth 2.0 Authorization Framework](https://datatracker.ietf.org/doc/html/rfc6749)
- [RFC 7636 — Proof Key for Code Exchange (PKCE)](https://datatracker.ietf.org/doc/html/rfc7636)
- [RFC 9700 — Best Current Practice for OAuth 2.0 Security](https://datatracker.ietf.org/doc/html/rfc9700)
- [OAuth 2.1 Draft (draft-ietf-oauth-v2-1)](https://datatracker.ietf.org/doc/draft-ietf-oauth-v2-1/)
- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
- [SAML 2.0 Core Specification (OASIS)](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf)
- [RFC 7519 — JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
- [RFC 7644 — SCIM Protocol](https://datatracker.ietf.org/doc/html/rfc7644)
- [RFC 8693 — OAuth 2.0 Token Exchange](https://datatracker.ietf.org/doc/html/rfc8693)
- [FAPI 2.0 Security Profile](https://openid.net/specs/fapi-2_0-security-profile.html)
- [W3C WebAuthn Level 3](https://www.w3.org/TR/webauthn-3/)
- [FIDO Alliance — Passkeys](https://fidoalliance.org/passkeys/)
- [Keycloak Documentation](https://www.keycloak.org/documentation)
- [Keycloak Release Notes](https://www.keycloak.org/docs/latest/release_notes/index.html)
현재 단락 (1/264)
The moment your internal systems exceed ten, and the moment SaaS adoption gets serious, every organi...