SAML vs OAuth vs OIDC: What Each One Is Actually For
These three get compared constantly, usually in a table that lists which one uses XML and which one uses JSON. That comparison is technically accurate and almost useless, because it implies the three are alternatives competing for the same job.
These three get compared constantly, usually in a table that lists which one uses XML and which one uses JSON. That comparison is technically accurate and almost useless, because it implies the three are alternatives competing for the same job.
They aren't. One of them isn't even an authentication protocol, which is the source of most of the confusion — and of a good number of production vulnerabilities.
Here's the version that actually helps you choose.
The one-sentence version
SAML is federation. It lets one organization's identity system vouch for a user to a different organization's application.
OAuth 2.0 is delegated authorization. It lets a user grant an application limited access to resources without handing over their credentials. It does not tell you who the user is.
OIDC is authentication, built as a thin layer on top of OAuth. It answers the question OAuth deliberately doesn't.
If you take away one thing: SAML and OIDC are alternatives to each other. OAuth is not an alternative to either — it solves a different problem, and OIDC is what happens when you extend it to solve this one.
Why OAuth isn't an authentication protocol
This is the misunderstanding worth spending time on, because it's both extremely common and genuinely dangerous.
OAuth was designed for a specific problem: you want to let a third-party application access some of your data on another service, without giving that application your password. You authorize a calendar app to read your email metadata. The app gets an access token scoped to that permission. It never sees your credentials.
Notice what an access token represents: permission to access a resource. It says "the bearer of this token may read this mailbox." It does not say "the bearer of this token is Alice," and it isn't addressed to the application holding it.
Here's the failure that follows from missing this. A developer implements "Log in with Provider X" using plain OAuth. They receive an access token, call the provider's userinfo-style endpoint, get back a user ID, and log that user in. It works in testing, so it ships.
The problem is that an access token is a bearer credential that the application has no way to validate the provenance of. If an attacker obtains an access token issued to a different application — from a malicious app the victim also authorized, from a leaked log, from a compromised client — they can present it to your app. Your app calls the userinfo endpoint, gets back the victim's identity, and logs the attacker in as the victim.
This is the confused deputy problem, and it's why the industry built OIDC rather than letting everyone keep improvising authentication on top of OAuth.
What OIDC adds
OIDC introduces the ID token: a JWT, addressed to a specific client, describing a specific authentication event.
The critical properties are the ones the access token lacks:
aud(audience) — names the client the token was issued to. If it isn't you, you reject it. This alone closes the attack above.iss(issuer) — who issued it, verified against a signature you can check.nonce— a value your application generated for this specific login attempt, echoed back in the token. It binds the token to your request, so a token captured from a different flow can't be replayed into yours.auth_time— when the user actually authenticated, which is what makesmax_ageand re-authentication enforceable.sub— a stable identifier for the user at this issuer.
An access token says what may be accessed. An ID token says who authenticated, when, and for whom this statement was issued. Different claims, different purposes.
flowchart LR
subgraph OAuth["OAuth 2.0"]
AT["Access token\n'bearer may read mailbox'\nAudience: the API"]
end
subgraph OIDC["OIDC = OAuth + identity layer"]
AT2["Access token\n(same as above)"]
IT["ID token\n'Alice authenticated at 14:02'\nAudience: your client"]
end
The practical rule: never use an access token to establish identity. If you're doing login, you want an ID token, and you want to validate its signature, issuer, audience, expiry, and nonce. Skipping any of those is how the vulnerability comes back.
Where SAML fits
SAML predates all of this — it comes from the early 2000s enterprise world — and it solves federation directly rather than as a layer on something else.
The core exchange: a user tries to reach an application (the Service Provider). The SP redirects them to their organization's Identity Provider. The user authenticates there, using whatever the organization requires. The IdP produces a signed XML assertion stating who the user is and what attributes they have, and the browser carries it back to the SP.
sequenceDiagram
participant U as User
participant SP as Application (SP)
participant IdP as Corporate IdP
U->>SP: Request resource
SP->>U: Redirect with AuthnRequest
U->>IdP: Authenticate
IdP->>U: Signed assertion (POST)
U->>SP: Deliver assertion
SP->>SP: Validate signature, conditions, audience
SP->>U: Session established
Structurally, this is the same shape as an OIDC login. A user is redirected to an authority, authenticates, and returns with a signed statement about who they are. The differences are in the encoding (XML with XML-DSig rather than JSON with JWS), the transport conventions, and about twenty years of accumulated enterprise deployment practice.
That last part is the reason SAML is not going anywhere. Every large enterprise has SAML infrastructure, SAML expertise, and a procurement checklist with SAML on it. "SAML is legacy" is a technically arguable position and a commercially irrelevant one — if you sell to enterprises, you will support SAML.
Choosing between them
Since SAML and OIDC solve the same problem, the choice is rarely about protocol elegance. It's about context.
Use SAML when:
- You're integrating with enterprise customers whose IdP is already configured for it — which is most of them
- Procurement or a security questionnaire explicitly requires it
- The customer's identity team knows SAML and doesn't want to learn something else for one vendor
Use OIDC when:
- You control both sides, or you're greenfield
- Mobile or single-page apps are involved — SAML's browser-redirect-and-POST model is genuinely awkward there
- You want the same protocol family for both user login and API authorization
- You need short-lived tokens and refresh semantics, which OIDC/OAuth handle natively
Use plain OAuth (no OIDC) when:
- There's no user identity involved at all — service-to-service, scheduled jobs, machine clients
- You're granting an application access to an API and genuinely don't need to know who the end user is
The realistic answer for most B2B platforms: support both SAML and OIDC for inbound federation, because you don't get to choose what your customers run. Use OIDC internally. Use OAuth for API authorization.
The distinctions people actually trip on
"We support OAuth so we support SSO." Not quite — OAuth gives you delegated API access. Enterprise SSO means federation, which means OIDC or SAML.
"We'll just use the access token to identify the user." The vulnerability described above. Use the ID token.
"OIDC replaced SAML." It didn't, and the evidence is every enterprise deal you'll close in the next five years. They coexist, and mature platforms speak both.
"SAML is just OIDC with XML." Close enough conceptually to be a useful intuition, wrong enough operationally to hurt you. SAML's signature validation is genuinely harder to get right — XML Signature Wrapping attacks have hit most major vendors at least once — and its certificate rotation story involves coordinating with a customer's IdP admin, which is a human problem, not a protocol one.
"We can skip the nonce." The nonce is what binds an ID token to your specific login request. Skipping it reopens a replay path that OIDC exists to close.
The summary worth keeping
| SAML | OAuth 2.0 | OIDC | |
|---|---|---|---|
| Answers | Who is this user? (across orgs) | May this app access this resource? | Who is this user? |
| Produces | Signed XML assertion | Access token | ID token (+ access token) |
| Identity claim | Yes | No | Yes |
| Typical use | Enterprise SSO | API authorization | Modern login, SSO |
| Alternative to | OIDC | nothing here | SAML |
OAuth sits in a different column on purpose. It's the foundation OIDC is built on, and it's the right tool when there's no user identity in the picture — but reaching for it to answer "who is this person" is reaching for a tool that was explicitly designed not to answer that.