Back to Blog
STANDARDS

OIDC vs SAML vs OAuth 2.0: A Developer's Comparison Guide (2026)

26 min readEmre Sakarya
SAML·OIDC·OAuth

Three federated identity protocols dominate enterprise authentication and authorization in 2026: SAML 2.0, OAuth 2.0 (now OAuth 2.1), and OpenID Connect. They are routinely confused, frequently misused, and surprisingly often deployed together inside the same application. The mental model most developers carry — that they are interchangeable single-sign-on protocols — is wrong in ways that produce real security incidents when an application ends up using OAuth for authentication, encoding session state in unverified JWT claims, or sharing access tokens across audiences that should not have seen them. This post walks the three protocols from a developer's perspective in their 2026 state — what each protocol is actually for, where they overlap and where they don't, the modern attack surface (OAuth 2.1 hardening, PKCE downgrade, SAML XML signature wrapping, assertion replay), and the implementation patterns that distinguish a working federated identity integration from one that ships with a class of bugs no scanner will catch. For the broader picture of how authentication fits into application security, see the OWASP A07 authentication failures guide and the ASVS 5.0 chapter on authentication.

Why Federated Identity Exists — The Mental Model

Before comparing the three protocols, it is worth resetting the underlying problem they are all trying to solve. An application needs to know two things about a user: who the user is (authentication) and what the user is permitted to do (authorization). The two questions are independent but related — authorization decisions usually depend on knowing the identity, but the identity check itself does not encode permission. Federated identity protocols address the same problem from one of two angles: the protocol either issues a verifiable identity assertion that the application can trust without checking credentials itself (SAML, OpenID Connect), or it issues a verifiable authorization grant that lets the application act on a user's behalf without seeing the user's credentials (OAuth 2.0/2.1).

The confusion that produces most federated-identity bugs is the conflation of these two angles. OAuth is an authorization protocol — it issues access tokens that say "the bearer of this token is permitted to perform action X against resource Y." It says nothing about who is acting; the access token does not encode identity, only authorization. OpenID Connect is the layer that adds identity on top — it issues ID tokens alongside OAuth access tokens, where the ID token is a verifiable claim about who the user is. SAML predates both and combines the two functions into a single assertion that is simultaneously an identity claim and an authorization context.

The practical consequence of this distinction is that an application that treats an OAuth access token as an authentication artifact — accepting "you have a valid access token" as proof of identity — has an authentication bypass class of bug, even if the underlying OAuth flow is correctly implemented. The access token does not say who the holder is; it says what the holder is permitted to do. Using it as an authentication claim is one of the most common federated identity mistakes, and one that no static analysis tool will flag because the bug is in the protocol semantics rather than the code.

SAML 2.0 — The XML Veteran Still Running Enterprise Identity

SAML 2.0 was finalized in March 2005 by the OASIS Security Services Technical Committee. Two decades later, it remains the dominant federated identity protocol in enterprise environments — particularly in workforce identity scenarios (employees logging into vendor SaaS through their corporate identity provider) where the dominant identity providers (Okta, Microsoft Entra ID formerly Azure AD, Ping Identity, Auth0/OneLogin) have all preserved strong SAML support alongside their newer OIDC offerings. SAML's persistence is partly historical (enterprise integrations once written tend to stay written) and partly substantive (SAML's assertion model handles certain enterprise scenarios more cleanly than OIDC's token model still does in 2026).

The SAML flow most developers will encounter in production is the SP-initiated SSO flow. The user visits the service provider (the application). The service provider issues a SAML AuthnRequest, redirecting the browser to the identity provider with the request as a base64-encoded SAML XML message in a URL parameter or form POST. The identity provider authenticates the user through whatever means it implements — password, MFA, certificate, biometrics — and responds with a SAML assertion: an XML document containing the user's identity attributes, the conditions under which the assertion is valid (time bounds, audience restriction, intended subject), and a digital signature over the assertion produced by the identity provider's signing key. The browser POSTs the signed assertion to the service provider, which verifies the signature, validates the conditions, and establishes the user's session.

SAML's strength is the assertion's self-contained nature. The assertion carries identity, attributes, authorization context, and validity conditions in a single signed document. The service provider does not call back to the identity provider; the assertion stands on its own, which makes SAML well-suited to scenarios where the identity provider is on a separate network from the service provider or where round-tripping back to the identity provider on every action would add latency the application can't absorb. SAML's weakness is XML — the message format is verbose, the signature scheme is complex, the canonicalization rules that make signatures verifiable are easy to implement incorrectly, and the historical pattern of XML processing vulnerabilities (XML External Entity injection, XML Signature Wrapping, XML comment injection) has produced a long tail of SAML-specific bugs.

The realistic 2026 verdict on SAML is that it remains the right choice for enterprise workforce SSO scenarios where the identity provider relationship is long-lived, the integration tolerates the operational complexity, and the assertion model fits the application's session needs. It is the wrong choice for consumer applications, for mobile-first applications, for service-to-service authentication, and for any scenario where the application benefits from a token-based architecture rather than an assertion-based one. Most modern applications that need both workforce SAML and a token-based internal architecture handle SAML at the edge — a SAML-aware identity gateway converts the SAML assertion into internal OIDC ID tokens — rather than threading SAML through the whole stack.

OAuth 2.0 (and 2.1) — The Authorization Standard

OAuth 2.0 (RFC 6749) was published in October 2012, intentionally replacing the earlier OAuth 1.0a protocol with a simpler, more deployable design. The simplification came with security tradeoffs that took the working group a decade to fully address; OAuth 2.1, currently in late-stage draft as of 2026, consolidates the security-hardening recommendations that accumulated across the OAuth security topic drafts (RFC 8252 for native apps, RFC 7636 for PKCE, the OAuth 2.0 Security Best Current Practice, the Browser-Based Apps draft) into a single coherent specification. For developers in 2026, OAuth 2.1's recommendations are effectively the security baseline; OAuth 2.0 vanilla without 2.1 hardening is the legacy mode most security-aware deployments are moving away from.

The mental model that produces the fewest OAuth-specific bugs is this: OAuth issues access tokens that let the holder act on a resource owner's behalf against a resource server. The four roles — resource owner (typically the human user), client (the application acting on the user's behalf), authorization server (the identity provider issuing tokens), and resource server (the API the client is accessing) — are distinct, even if multiple roles are sometimes implemented in the same physical service. The protocol's flows describe how the client obtains an access token from the authorization server with the resource owner's consent.

OAuth 2.0 defined four grant types: authorization code, implicit, resource owner password credentials, and client credentials. OAuth 2.1 removes the implicit grant and the resource owner password credentials grant entirely — both had security profiles that the working group ultimately judged to be irrecoverable. The remaining grants are authorization code (with PKCE always required, formerly optional), client credentials (for service-to-service scenarios with no user present), and the new device authorization grant for input-constrained devices.

The authorization code flow with PKCE is the grant developers should default to for any user-facing application — web, mobile, single-page application, desktop. The flow runs in four stages. First, the client generates a code verifier (a high-entropy random string) and a code challenge (the SHA-256 hash of the verifier, base64url-encoded), then redirects the browser to the authorization server's authorization endpoint with the code challenge, the requested scopes, a redirect URI, and a state parameter for CSRF protection. Second, the authorization server authenticates the user and obtains consent, then redirects the browser back to the registered redirect URI with an authorization code and the state parameter echoed back. Third, the client exchanges the authorization code for an access token at the authorization server's token endpoint, sending the code verifier in the exchange request — the authorization server hashes the verifier and matches it against the code challenge from the initial request, which is the PKCE protection against authorization code interception attacks. Fourth, the client uses the access token to call the resource server.

// Authorization Code + PKCE — client-side (illustrative)
const verifier = base64url(crypto.getRandomValues(new Uint8Array(32)))
const challenge = base64url(await sha256(verifier))

sessionStorage.setItem('pkce_verifier', verifier)
sessionStorage.setItem('oauth_state', randomState)

const authUrl = new URL('https://idp.example.com/authorize')
authUrl.searchParams.set('response_type', 'code')
authUrl.searchParams.set('client_id', CLIENT_ID)
authUrl.searchParams.set('redirect_uri', REDIRECT_URI)
authUrl.searchParams.set('scope', 'openid profile email')
authUrl.searchParams.set('code_challenge', challenge)
authUrl.searchParams.set('code_challenge_method', 'S256')
authUrl.searchParams.set('state', randomState)

window.location.assign(authUrl.toString())

The single most common OAuth implementation bug is improper validation of the redirect URI on the authorization server side. The authorization code is delivered to a URI registered with the authorization server during client setup; if the authorization server accepts a redirect URI that matches the registered URI only by prefix rather than by exact comparison, an attacker can register a malicious URI like https://yourapp.com.attacker.com/callback and steal the authorization code. Authorization servers must perform exact-match comparison of redirect URIs, including the scheme, host, port, and path. Clients must register the exact redirect URI they will use, including the path component.

The second-most-common OAuth bug is failure to validate the state parameter on the redirect back to the client. The state parameter is the CSRF protection for the OAuth flow — without it, an attacker can initiate an OAuth flow against the victim's authorization server and then deliver the resulting authorization code to the victim's session, causing the victim to inadvertently link their session to the attacker's account. Clients must generate a high-entropy state value, store it in the user's session before redirecting to the authorization server, and verify on the callback that the returned state matches the stored value.

The third bug class is mishandling the access token after issuance. Access tokens are bearer credentials — anyone in possession of a token can use it for the duration of its validity. Storing access tokens in localStorage exposes them to any cross-site scripting vulnerability in the application; the better pattern for browser-based clients is to keep access tokens in memory (re-fetching as needed via the silent renewal flow) and store only refresh tokens in HttpOnly, Secure, SameSite cookies bound to the auth-server domain. The native mobile and desktop equivalents are platform secure storage (Keychain on iOS, Keystore on Android, the OS credential manager on desktop).

OpenID Connect — Identity as a Layer Over OAuth

OpenID Connect (OIDC), finalized in February 2014, is an identity layer built on top of OAuth 2.0. It reuses OAuth's flows, endpoints, and tokens, and adds: an ID token (a JWT carrying verifiable claims about the authenticated user), a UserInfo endpoint (an OAuth-protected resource that returns the user's identity attributes), a standardized set of scopes (openid, profile, email, address, phone), and a discovery mechanism (the /.well-known/openid-configuration endpoint that describes the identity provider's capabilities). The design intent was to provide an identity protocol that any OAuth client could implement with minimal additional complexity, replacing the older OpenID 2.0 protocol that had failed to gain traction.

The ID token is the artifact that distinguishes OIDC from raw OAuth. It is a signed JWT containing claims about the user — the subject identifier (sub), the issuer (iss), the audience (aud, the client ID that requested the token), the issuance and expiration times (iat, exp), and a nonce that ties the token to a specific authentication request. The client verifies the ID token's signature using the identity provider's published JWKS (JSON Web Key Set), validates the standard claims, and reads the subject and additional identity claims to establish the user's session.

The ID token is for the client; the access token is for the resource server. This is the distinction that produces the most OIDC implementation bugs. The ID token is meant to be consumed by the client that requested it; passing the ID token to a downstream API as an authentication token is a misuse that may or may not be exploitable depending on the API's audience-claim validation. The access token is meant to be sent to resource servers; using it on the client to read user identity claims requires either an additional UserInfo endpoint call or trust in unverified token payload data that may not be intended as identity assertion.

The nonce claim is OIDC's defense against ID token replay. The client generates a nonce alongside the OAuth state parameter, includes it in the authorization request, and verifies on receipt that the ID token's nonce claim matches. Without nonce verification, an attacker who captures an ID token from one authentication flow can replay it into another, causing the victim's client to accept a stale or attacker-influenced identity claim. The nonce is to ID token replay what the state is to authorization code CSRF — both must be generated fresh per flow and validated on receipt.

OpenID Connect vs SAML vs OAuth: The Decision Frame

The OpenID Connect vs SAML question dominates federated-identity decisions in 2026 because both protocols can satisfy the same workforce SSO use case, with substantively different architectural consequences. The OAuth vs OIDC question is structurally different — OAuth and OIDC are not alternatives but layers, with OIDC adding identity assertions on top of OAuth's authorization primitives. The OAuth vs OIDC framing only confuses developers because the protocols overlap in the OAuth-authorization-code-with-PKCE flow that OIDC reuses, but the artifacts they produce (access tokens vs ID tokens) and the questions they answer (what can the holder do vs who is the holder) are distinct. A side-by-side comparison helps frame the openid connect vs saml decision and the oauth vs oidc relationship in one view.

The variants of the comparison question developers ask reflect the same underlying decision from different angles. The SAML vs OAuth vs OpenID framing centers the three protocols as alternatives — useful for an architectural decision but slightly misleading because OAuth and OIDC are not alternatives to each other. The saml vs oauth vs openid phrasing is the same comparison; the saml vs oauth2 and openid vs oauth2 variants narrow the comparison to two protocols at a time. The OIDC vs OAuth variant (without SAML) focuses on the authentication-vs-authorization distinction within the modern token-based stack. The OAuth 2.0 vs SAML comparison asks the legacy-vs-modern federated identity question — when is the older XML-based standard still the right answer, when is the newer token-based stack better aligned to the application's needs. All these variants map onto the same underlying decision criteria covered below.

Side-by-Side Comparison

CriterionSAML 2.0OAuth 2.0/2.1OpenID Connect
Primary purposeIdentity assertion + SSODelegated authorizationIdentity on top of OAuth
Token formatSigned XML assertionOpaque or JWT access tokenJWT ID token + OAuth access token
Carries identityYes (in assertion)No (access token is authorization only)Yes (in ID token)
Carries authorizationYes (in attributes)Yes (in access token scopes)Yes (via underlying OAuth)
Standard year20052012 (2.0), 2.1 in late draft 20262014
Transport bindingHTTP POST or RedirectHTTP redirects + token endpointHTTP (same as OAuth)
Typical use caseEnterprise workforce SSOThird-party API accessModern web / mobile / SPA login
Mobile-friendlyNo (XML-heavy, browser-bound)Yes (with PKCE)Yes (inherits OAuth)
Service-to-servicePossible but rareYes (client credentials grant)Yes (via OAuth client credentials)
Standards bodyOASISIETFOpenID Foundation
Common identity providersOkta, Entra ID, Ping, Auth0Same + consumer (Google, GitHub, Facebook)Same + consumer
Token revocationHard (assertions are stateless)Token revocation endpoint (optional)Inherits OAuth revocation
Best forWorkforce SSO into SaaSApp acting on user's behalf against APIModern app needing user login

When to Use Which — Decision Tree

The decision among the three protocols rarely needs to be made from scratch; it is usually constrained by the identity provider the organization already runs and the application's primary use case. The pragmatic decision tree:

Is this a workforce SSO scenario where employees log in through a corporate identity provider? If yes, the identity provider almost certainly supports both SAML and OIDC; pick OIDC if the application is modern (built post-2015) and tolerates JWT-based architecture, pick SAML if the application is legacy (built pre-2015) or operates in a long-lived enterprise integration that already standardizes on SAML. If you can choose freely, prefer OIDC for new builds — its token model is easier to reason about, its mobile and SPA support is native, and its ecosystem tooling is more current.

Is this a consumer-facing application accepting login through Google, GitHub, Facebook, Apple, or similar? Use OIDC. All major consumer identity providers expose OIDC endpoints, and the protocol is purpose-built for this scenario.

Is this an application acting on a user's behalf against a third-party API (Google Drive, GitHub, Stripe)? Use OAuth 2.0/2.1 authorization code with PKCE. The third-party API will document its OAuth flow; follow it precisely. The identity layer (if needed) is provided by the third party's OIDC equivalent or by your own application's session.

Is this service-to-service authentication between backend components in your own architecture? Use OAuth client credentials grant with strong client authentication (mutual TLS or signed JWT assertion). The client credentials grant is OAuth's service-to-service flow; there is no user, no consent step, no browser involvement. mTLS-bound access tokens are increasingly the standard for high-trust service-to-service scenarios in 2026.

Is this a mobile or single-page application? OAuth 2.1 (or OIDC) authorization code with PKCE is mandatory. The implicit grant is deprecated; the resource owner password credentials grant is deprecated; PKCE is required (not optional) in OAuth 2.1.

The cases where the decision is genuinely difficult are usually mixed scenarios — workforce SSO via SAML at the edge with internal OIDC tokens, or consumer OIDC login with enterprise SAML for B2B portal access. These are not bugs in the protocols; they are integration patterns. The pragmatic approach is to handle the protocol at the boundary it natively supports and convert to a single internal identity model behind that boundary.

The Modern Attack Surface — 2026 Update

The federated identity attack surface evolved substantially between 2019 (when most widely-cited comparison posts were written) and 2026. The categories of attack worth knowing in current form:

OAuth authorization code interception. Without PKCE, an attacker who can intercept the redirect from the authorization server to the client (on a mobile device with a malicious app registering the same URL scheme, on a desktop application proxying the loopback redirect, or in any scenario where the redirect URI is not securely bound to the requesting client) can steal the authorization code and exchange it for tokens. PKCE addresses this by binding the authorization request to a verifier known only to the original client. OAuth 2.1 makes PKCE mandatory; OAuth 2.0 made it optional and many implementations omitted it.

PKCE downgrade attacks. An attacker controlling part of the authorization flow can downgrade a PKCE-using client to a non-PKCE flow if the authorization server doesn't enforce PKCE on the token endpoint side. The OAuth 2.1 hardening is to refuse the token request if a client that registered as PKCE-using sends a code_verifier mismatch or omission. Authorization servers that accept the absence of PKCE silently undermine the protection.

SAML XML Signature Wrapping (XSW). A signed SAML assertion can be wrapped in additional XML structure such that the signature verifies against the original signed element while the application reads attributes from a substituted element. The defense is strict canonicalization and processing the signed element rather than re-querying the XML for attributes. Most modern SAML libraries handle this correctly; rolled-your-own SAML processing very often does not.

SAML assertion replay. A captured SAML assertion remains valid until its NotOnOrAfter timestamp expires. Without short validity windows and audience restrictions, a stolen assertion can be replayed against the service provider. The defense is short validity, audience restriction enforced at the service provider, and (where the threat model warrants) one-time-use assertion tracking.

SAML metadata poisoning. SAML trust is anchored in metadata documents that publish each side's signing keys, endpoints, and configuration. An attacker who can inject a malicious metadata document into a service provider's trust store can substitute a controlled identity provider's signing keys for the legitimate ones. The defense is signed metadata distributed through trustworthy channels, metadata signature verification, and periodic metadata refresh from authoritative sources.

ID token misuse as access token. The OIDC ID token is meant to be consumed by the client that requested it. Forwarding it to a downstream API as a bearer token is a misuse — if the API doesn't validate the audience claim, the misuse may succeed. The defense at the API side is strict audience claim validation; the defense at the client side is using access tokens for resource access and ID tokens only for client-side identity establishment.

Open redirect chained with OAuth. A client application with an open redirect can be chained with an OAuth flow to deliver tokens to attacker-controlled URLs. The defense is exact-match redirect URI registration and validation at both the client and the authorization server.

JWT signature confusion. A JWT signed with one algorithm can be re-presented as a JWT signed with a different algorithm, exploiting libraries that select the verification algorithm based on the JWT header rather than the configured algorithm. The defense is to fix the verification algorithm at the application configuration level rather than letting the JWT header dictate it; many modern JWT libraries default to this behavior but some still allow header-driven algorithm selection. The JWT vulnerabilities guide covers the full taxonomy.

Implementation Pitfalls — The Things Scanners Won't Catch

Static and dynamic analysis tools find some classes of federated identity bugs — predictable state values, missing redirect URI validation, JWT signature verification failures. They miss most of the high-impact bugs, which are in the protocol semantics rather than in the code that implements them. The pitfalls worth catching in code review:

Access tokens used for identity. An application that reads claims from an access token to establish "who the user is" is making an authentication decision on an authorization artifact. The fix is to use the ID token (in OIDC scenarios) or to make a UserInfo endpoint call (when only access tokens are available); either way, identity must come from an artifact that is meant to carry identity.

Missing audience validation. A JWT may be signed by a trusted issuer but intended for a different audience — for example, a token issued for a different microservice in the same domain. Validating the signature alone is insufficient; the audience claim must match the consuming service's identifier. This is a particular hazard in microservice architectures where multiple services share the same identity provider but have distinct audience identifiers.

Missing nonce validation in OIDC. The nonce binds an ID token to a specific authentication flow. Clients that don't generate a nonce in the authorization request, or that don't validate the returned nonce on the ID token, lose the protection against ID token replay. The pattern is to generate a nonce per flow, store it in session, and verify on the ID token receipt that it matches.

State parameter not bound to session. The state parameter prevents CSRF on the OAuth callback. If state is generated but not bound to the user's session (for example, stored in a generic hash table keyed by state value), an attacker can replay state in their own session. The pattern is to bind state to the session — store it in the session storage and check at callback time that the session has a state value matching the callback's state.

Refresh tokens not rotated. Long-lived refresh tokens that are not rotated on use create a window where a stolen refresh token can be used indefinitely. The OAuth 2.1 recommendation is to rotate refresh tokens on every use — the authorization server issues a new refresh token alongside the new access token and invalidates the previous refresh token. Detection of a previously-invalidated refresh token presentation indicates token theft and should trigger token revocation for the entire session.

Token storage in localStorage. Access tokens or refresh tokens stored in localStorage are accessible to any cross-site scripting vulnerability in the application. The pattern for browser-based clients is to keep access tokens in memory and refresh tokens in HttpOnly, Secure, SameSite cookies bound to the authorization server domain — or, in the strict-cookies model, to use a backend-for-frontend pattern where the browser holds only a session cookie and tokens are kept server-side.

Logout that doesn't terminate session. An application logout that clears the local session but does not call the OIDC RP-initiated logout endpoint or the OAuth token revocation endpoint leaves valid tokens in the wild. For high-security applications, logout must terminate the identity provider session and revoke outstanding tokens, not just clear the local session state. The session hijacking guide covers the session-termination pattern in depth.

Service-to-Service — mTLS and the Client Credentials Grant

The federated identity protocols above assume a human user is present in the flow. Modern microservice architectures need an equivalent for service-to-service authentication where no human is involved — service A calling service B in the same trust domain. OAuth's client credentials grant is the standard solution: service A authenticates to the authorization server as a client (using either a strong client secret, a signed JWT assertion, or mutual TLS), obtains an access token, and uses it to call service B. Service B validates the access token's signature, audience, and scopes before accepting the request.

The 2026 hardening pattern is mTLS-bound access tokens. The authorization server binds the issued access token to the certificate used to authenticate at the token endpoint; the resource server validates the same certificate is presented when the token is used. This binding prevents a stolen access token from being usable from any host other than the original client's mTLS-authenticated identity. The pattern requires PKI infrastructure that not every organization runs, but where it is available, it raises the bar against access token theft substantially.

The alternative pattern that does not require mTLS is DPoP (Demonstrating Proof of Possession). The client presents a signed JWT alongside each access token use, demonstrating possession of a private key that the access token is bound to. The resource server validates the DPoP JWT alongside the access token. DPoP is appropriate where mTLS is not available; it adds per-request overhead but is implementable with public-key cryptography that does not require PKI.

What This Means for Your Application in 2026

For most applications in 2026, the recommended federated identity stack is the following. For workforce SSO, OIDC with the corporate identity provider, with SAML as a fallback only where the integration cannot be modernized. For consumer login, OIDC with the appropriate consumer identity providers (Google, Apple, GitHub). For application-acting-on-user's-behalf against third-party APIs, OAuth 2.1 authorization code with PKCE, following the third-party API's specific OAuth documentation. For service-to-service, OAuth client credentials grant with mTLS-bound access tokens where the PKI is available, with DPoP-bound tokens otherwise. JWT-format access tokens with audience claims for stateless service-to-service authorization, opaque tokens where token introspection at the resource server is acceptable.

The single most important implementation discipline is to use a maintained identity library appropriate to the application's language and framework rather than rolling protocol handling by hand. Auth0's universal libraries, Microsoft's MSAL family, Google's identity client libraries, Spring Security OAuth, the Authlib family for Python — these are all maintained against the current security profiles and updated as protocol hardening recommendations evolve. Roll-your-own SAML or OAuth implementations almost always omit one or more of the defenses described above; the cost of fixing the omitted defenses retroactively exceeds the cost of using a library from the start.

The deeper migration pattern for organizations with mixed SAML/OAuth/OIDC estates is to standardize on OIDC as the internal token model and handle SAML only at the edge for workforce integrations that cannot be modernized. This produces a single internal authentication model — JWT-based, OIDC-aligned, easier to audit and easier to reason about — while preserving SAML compatibility for the long-tail enterprise integrations that will remain SAML-bound for the foreseeable future.

Where to Go Deeper

The SAML implementation pitfalls — XML Signature Wrapping, assertion replay, metadata poisoning, signature stripping — have enough operational depth that they merit a separate post. The SAML 2.0 implementation pitfalls deep dive walks the SAML-specific attack surface and the implementation patterns that close each class of bug. For the broader authentication and session-management requirements that ASVS 5.0 makes verifiable, the ASVS 5.0 developer guide covers the V6 and V7 chapter material that any federated identity integration must satisfy. For JWT-specific vulnerabilities (signature confusion, algorithm confusion, claim manipulation, key confusion), the JWT vulnerabilities guide walks the full taxonomy with code-level examples.

OIDC vs SAML vs OAuth: questions developers ask

What is the difference between OIDC and OAuth?

OAuth 2.0 is an authorization protocol — it issues access tokens that let a client act on a user's behalf against an API. OAuth says nothing about who the user is. OpenID Connect is an identity layer built on top of OAuth — it adds an ID token (a signed JWT) that carries verifiable claims about the authenticated user. The most common bug in this space is using an OAuth access token as an authentication artifact, which is a misuse since the access token does not encode identity.

SAML vs OAuth: when should I use which?

SAML is the right choice for enterprise workforce SSO where the integration is long-lived, the identity provider is a corporate IdP (Okta, Entra ID, Ping), and the application is browser-based. OAuth (with PKCE in 2.1) is the right choice for any scenario involving mobile clients, SPAs, third-party API access, or service-to-service authentication. Most modern applications that need both run SAML at the edge for workforce SSO and convert to internal OIDC tokens behind that boundary.

What is PKCE and do I need it?

PKCE (Proof Key for Code Exchange) is an OAuth extension that binds an authorization request to a verifier known only to the original client. It prevents authorization code interception attacks in scenarios where the redirect to the client cannot be securely bound to that client (mobile apps, SPAs, desktop applications). OAuth 2.1 makes PKCE mandatory; OAuth 2.0 made it optional. You need PKCE for any client other than a confidential server-side client running over HTTPS where the redirect URI is securely bound — which in practice means almost every modern client needs PKCE.

Is OAuth 2.1 ready to use?

OAuth 2.1 is a consolidation of OAuth 2.0 plus the security hardening recommendations (mandatory PKCE, removal of implicit grant and resource owner password credentials grant) that accumulated as separate drafts. As of 2026 it is in late-stage draft. The practical impact is that the OAuth 2.1 recommendations are already the security baseline for new deployments — most identity providers and libraries implement them whether they are formally called OAuth 2.1 or OAuth 2.0 with hardening.

Where should I store OAuth tokens in a browser?

Access tokens should be kept in memory (re-fetched via silent renewal as needed). Refresh tokens should be in HttpOnly, Secure, SameSite cookies bound to the authorization server's domain. Avoid localStorage for either; any cross-site scripting vulnerability in the application will expose tokens stored there. The strict-cookies pattern uses a backend-for-frontend service where the browser holds only a session cookie and tokens are kept server-side.

What is the SAML XML Signature Wrapping attack?

XML Signature Wrapping (XSW) wraps a signed SAML assertion in additional XML structure such that the signature verifies against the original signed element while the application reads attributes from a substituted element. The defense is to process attributes from the signed element specifically, not to re-query the XML for attributes after signature verification. Modern SAML libraries typically handle this; hand-rolled SAML processing very often does not.

Do I need mTLS for service-to-service OAuth?

mTLS-bound access tokens are the high-trust pattern for service-to-service authentication in 2026 — they prevent a stolen access token from being usable from any host other than the original mTLS-authenticated client. Where mTLS PKI is not available, DPoP (Demonstrating Proof of Possession) provides similar bearer-token theft protection using public-key cryptography. Either pattern is preferable to plain bearer tokens for production service-to-service traffic.