Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

SAML vs OIDC for SSO

By Kokil Thapa | Last reviewed: September 2026

Choosing between SAML vs OIDC for SSO is one of the first architecture calls you make when a client asks for single sign-on. SAML 2.0 still dominates enterprise IdPs like Azure AD, Okta, and Google Workspace SAML apps. OpenID Connect sits on OAuth 2.0 and fits modern SPAs, mobile clients, and API-first stacks. If you build REST APIs and Laravel backends, the wrong protocol adds weeks of integration pain. This guide compares both protocols with real flows, a decision table, and production-oriented Laravel notes drawn from client portals and booking systems I've shipped since 2010.

What is the difference between SAML and OIDC for SSO?

SAML (Security Assertion Markup Language) and OIDC (OpenID Connect) both let users sign in once at an identity provider (IdP) and access multiple apps. They solve the same business problem with different wire formats and client expectations.

SAML 2.0 exchanges XML documents called assertions. The service provider (SP) trusts a signed assertion that states who the user is and which attributes they carry. Flows are almost always browser-based. HTTP-POST and HTTP-Redirect bindings are the common ones.

OIDC is an identity layer on top of OAuth 2.0. After OAuth authorisation, the IdP returns a signed JWT called an ID token. Optional access tokens authorise API calls. JSON, REST endpoints, and bearer tokens make OIDC natural for SPAs and mobile apps.

SAML vs OIDC for SSO — Core DifferencesSAML 2.0XML assertionsBrowser POST / RedirectEnterprise IdPsOpenID ConnectJWT ID tokensOAuth 2.0 + RESTWeb + mobile + APIsShared goal: federated identityUser signs in once at IdPSP trusts IdP-issued identity proof
SAML vs OIDC for SSO — XML enterprise assertions versus JWT-based OAuth identity layer

For deeper OIDC mechanics, see our OpenID Connect explained guide. SAML remains the default when IT hands you a metadata XML file and expects ACS URL configuration in Azure or Okta.

CriteriaSAML 2.0OpenID Connect
Payload formatXML assertion (signed/encrypted)JWT ID token (+ optional access token)
TransportBrowser POST, Redirect, ArtifactOAuth 2.0 authorisation code, PKCE, implicit (legacy)
Primary clientsBrowser SSO to web appsWeb, mobile, machine-to-machine APIs
Token lifetimeShort-lived assertion per loginID token + refresh token patterns
LogoutSLO (Single Logout) — often partialEnd-session endpoint — provider-dependent
Enterprise adoptionVery high (ADFS, Azure SAML, Okta SAML)High and growing (Azure OIDC, Auth0, Keycloak)
Developer ergonomicsXML metadata, certificate rotation painJSON discovery, standard JWT libraries
API authorisationNot designed for bearer API callsAccess tokens map cleanly to resource servers

The table is the short answer most architects want. SAML wins on enterprise compatibility. OIDC wins on developer velocity and API ecosystems.

How does SAML 2.0 SSO work step by step?

SAML SP-initiated login is the flow you see when a user hits your app's login page and clicks "Sign in with company account." The SP sends an AuthnRequest. The IdP authenticates the user. The IdP posts a SAML Response back to your Assertion Consumer Service (ACS) URL.

SAML 2.0 SP-Initiated SSO FlowUser BrowserService ProviderYour Laravel appIdentity ProviderAzure / Okta1. Visit /login2. Redirect + AuthnRequest3. User authenticates at IdP4. POST SAML Response5. POST to ACS URL6. Validate signature, create session
SAML 2.0 SP-initiated flow — AuthnRequest, IdP login, and ACS POST with signed assertion

Validate the assertion on your server

Never trust a SAML Response without cryptographic validation. Your SP must verify the XML signature against the IdP certificate from metadata. Check NotBefore and NotOnOrAfter conditions. Confirm the Audience restriction matches your entity ID. Match the InResponseTo value when you sent an AuthnRequest.

A minimal validation checklist on a production Laravel application looks like this:

  1. Fetch and cache IdP metadata XML (entity ID, SSO URL, signing cert).
  2. Configure SP entity ID and ACS URL — must match exactly what IT registered.
  3. Parse the SAML Response, verify signature with IdP X.509 cert.
  4. Extract NameID (often email) and attribute statements (groups, department).
  5. Map attributes to a local user record and start a Laravel session.
  6. Log assertion ID to prevent replay if your library supports it.

SAML metadata is XML-heavy. I keep a copy in version control and diff it when enterprise IT rotates certificates. Missing a cert rotation breaks login for every user at once. That failure mode is common on long-running client portal projects tied to corporate IdPs.

The official SAML 2.0 core specification is maintained by OASIS. Microsoft's Entra ID SAML documentation is a practical reference for Azure-specific attribute naming. Both are worth bookmarking during integration.

How does OpenID Connect SSO work in a modern web app?

OIDC adds identity to OAuth 2.0. The authorisation server returns an ID token (JWT) that proves who the user is. An access token may authorise API calls separately. Discovery via /.well-known/openid-configuration gives you endpoints and supported scopes without manual XML exchange.

OIDC Authorisation Code + PKCE FlowClient AppAuth ServerToken EPUser AgentAPI1. /authorize + code_challenge2. User login + consent3. Redirect with code4. POST /token + verifier5. ID token + access token6. Bearer access token to API
OpenID Connect authorisation code flow with PKCE — standard pattern for SAML vs OIDC for SSO decisions on modern apps

Validate the ID token

OIDC validation is JWT-based. Fetch JWKS from the IdP. Verify signature, iss, aud, and exp. Reject tokens with excessive clock skew. Use nonce in implicit-like flows; with authorisation code + PKCE, nonce is still recommended for hybrid setups.

Example discovery and token exchange pattern (PHP 8.3+, Laravel 12 or 13):

GET https://login.example.com/.well-known/openid-configuration

POST https://login.example.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=AUTH_CODE_HERE
&redirect_uri=https://app.example.com/auth/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=PKCE_VERIFIER_FROM_SESSION

Decode the ID token only after signature verification against JWKS. Map sub as the stable external identifier. Use email only when the IdP marks it verified. Our GitHub Actions OIDC guide covers machine identity with the same OAuth family — useful when comparing human SSO with workload federation.

The OpenID Connect Core 1.0 specification defines standard claims and validation rules. Pair it with openid.net's official OIDC core spec and the IETF OAuth 2.0 RFC for authoritative definitions.

When should you choose SAML vs OIDC for SSO?

Protocol choice is constrained by what the IdP and the customer IT team already support. Greenfield products favour OIDC. Enterprise procurement often mandates SAML.

SAML vs OIDC Decision TreeNew SSO requirement?IdP offers SAMLonly?SPA / mobile / API?first product?Choose SAML 2.0Enterprise web SSOChoose OIDCTokens + API accessBoth supported? Prefer OIDC for new code
Decision tree for SAML vs OIDC for SSO — enterprise constraints versus modern client and API needs
  • Choose SAML when the customer's IdP app catalogue is SAML-only, you integrate with legacy ADFS, or procurement documents specify SAML 2.0 federation.
  • Choose OIDC when you ship a SPA, mobile app, or Laravel API with Sanctum/Passport, or when you need refresh tokens and fine-grained scopes.
  • Support both when you sell B2B SaaS to mixed enterprises — common on enterprise application builds with separate SAML and OIDC connectors.
  • Avoid SAML for native mobile unless you wrap it in a WebView — UX and SLO are brittle.
  • Avoid OIDC-only assumptions with law firms and banks in Nepal — IT vendors often deliver SAML metadata first; plan attribute mapping early.

On legal-tech portals like Court Marriage In Nepal, staff SSO is rare at small firms. Enterprise clients requesting staff dashboards are where SAML appears. Public-facing social login is OIDC via Google or Microsoft — different threat model, same protocol family.

How do you implement SSO in Laravel with SAML or OIDC?

Laravel does not ship SAML or OIDC in core. Composer packages and Socialite drivers fill the gap. Pick packages with active maintenance and clear certificate handling.

OIDC with Laravel Socialite

For OIDC providers exposing OAuth2 endpoints (Google, Azure OIDC, Keycloak), Socialite plus a provider-specific driver is the fastest path on Laravel 12 or 13 with PHP 8.3+.

composer require laravel/socialite
composer require socialiteproviders/microsoft-azure

Route::get('/auth/redirect', fn () =>
    Socialite::driver('azure')->scopes(['openid', 'profile', 'email'])->redirect()
);

Route::get('/auth/callback', function () {
    $oidcUser = Socialite::driver('azure')->user();
    $user = User::updateOrCreate(
        ['external_id' => $oidcUser->getId()],
        ['email' => $oidcUser->getEmail(), 'name' => $oidcUser->getName()]
    );
    Auth::login($user, true);
    return redirect('/dashboard');
});

For first-party APIs, pair OIDC login with Laravel Sanctum or Passport. Issue your own API tokens after federated login. Do not expose the upstream IdP access token to browser clients unless you accept token passthrough risk.

SAML with a dedicated package

SAML in Laravel typically uses aacotroneo/laravel-saml2 or 24slides/laravel-saml2. Configuration spans config/saml2, routes for ACS, and metadata export for the IdP admin.

composer require 24slides/laravel-saml2

SAML2_IDP_ENTITYID=https://sts.windows.net/TENANT-ID/
SAML2_IDP_SSO_URL=https://login.microsoftonline.com/TENANT-ID/saml2
SAML2_IDP_x509=MIIC...base64-cert...
SAML2_SP_x509=MIIC...your-sp-cert...
SAML2_SP_PRIVATEKEY=-----BEGIN PRIVATE KEY-----...

Publish SP metadata at the URL your IdP admin expects. Map SAML attributes in an event listener after login:

Event::listen(SignedIn::class, function (SignedIn $event) {
    $samlUser = $event->getSaml2User();
    $email = $samlUser->getAttribute('http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress')[0] ?? null;
});

Test SAML with dummy IdP tools before involving client IT. Validate clock sync on your Ubuntu server — skew breaks NotOnOrAfter checks silently. NTP matters on Linux production hosts the same way it matters for JWT exp validation.

Session hardening shared by both protocols

After federated login, you still own session security. Regenerate session ID on login. Set secure, HttpOnly, SameSite cookies. Apply authorisation policies after authentication — SSO proves identity, not permission to billing or admin modules.

Use our password generator for service account secrets. Store IdP client secrets and SAML private keys outside git. Laravel's .env plus encrypted env on Deployer releases is the pattern I use on GitLab CI pipelines.

What are common SAML vs OIDC deployment mistakes?

Most SSO outages are configuration drift, not protocol bugs. These failures repeat across client projects.

  • ACS / redirect URI mismatch — trailing slashes, http vs https, or wrong subdomain. IdPs treat URLs as exact strings.
  • Certificate expiry without monitoring — SAML signing certs and SP certs expire quietly. Alert 30 days ahead.
  • Attribute mapping assumptions — Azure sends emailaddress claim URIs; Okta uses different keys. Log raw attributes once in staging.
  • Confusing authentication with authorisation — SSO group claims need mapping to local roles. Spatie Permission still applies.
  • Using OIDC implicit flow in new apps — prefer authorisation code with PKCE for public clients per current OAuth best practice.
  • Ignoring single logout — users expect global logout; SAML SLO and OIDC end-session are inconsistently supported. Document actual behaviour.
  • Passing IdP tokens to the frontend — increases leak surface. Exchange for a first-party session or Sanctum token.

Debug SAML responses with browser devtools on the ACS POST — base64-decode only in staging. For JWTs, paste into a local JSON formatter after redacting secrets — never log full tokens in production.

Rate-limit login and callback routes. Federated endpoints attract abuse similar to password forms. See API rate limiting patterns for shared middleware ideas.

The OASIS SAML 2.0 technical overview remains the authoritative SAML reference. For Azure-specific SAML claim URIs, Microsoft's identity platform docs beat generic tutorials.

Key Takeaways

  • SAML 2.0 fits enterprise browser SSO with XML metadata; OIDC fits modern web, mobile, and API stacks with JWTs.
  • Validate SAML signatures and OIDC JWTs on the server — never skip issuer, audience, and expiry checks.
  • Prefer OIDC with authorisation code + PKCE for new Laravel apps unless the IdP mandates SAML.
  • Map external attributes explicitly; log staging assertions once to catch IdP-specific claim names.
  • Monitor IdP and SP certificate expiry — it is the most common production SSO outage.
  • After SSO login, enforce local authorisation with Laravel policies and roles independent of the IdP.

People Also Ask

Is OIDC replacing SAML?

OIDC is the default for new cloud and developer-centric integrations, but SAML is not dead. Large enterprises standardised on SAML for years. Many IdPs support both. New SaaS products often ship OIDC first and add SAML for enterprise sales cycles.

Can one app support both SAML and OIDC?

Yes. B2B platforms frequently expose a SAML connector for corporate IdPs and OIDC for Google/Microsoft social or partner APIs. Keep user records linked by a stable external ID per provider. Document which tenant uses which protocol.

Which is more secure, SAML or OIDC?

Neither is inherently more secure. Security depends on TLS everywhere, correct signature validation, PKCE for public OIDC clients, short token lifetimes, and secret rotation. SAML's XML complexity increases misconfiguration risk. OIDC's JWT ecosystem is easier to audit with standard libraries.

Does Laravel Sanctum replace OIDC?

No. Sanctum issues tokens for your application's API after a user is authenticated. OIDC federates authentication to an external IdP. Typical pattern: OIDC or SAML for login, then Sanctum for SPA API sessions on your domain.

Pick the protocol your IdP and product actually need

SAML vs OIDC for SSO is not a popularity contest. Read the customer's IdP documentation first. Ship OIDC when you control both sides or target modern clients. Implement SAML when enterprise metadata arrives before your first sprint ends. On greenfield custom software projects, I default to OIDC for Laravel APIs and add SAML when contract requirements appear — not the other way around.

If you are planning federated login for a portal, booking platform, or internal dashboard, map IdP constraints before writing auth code. Contact us for architecture review, or browse the Adventure Third Pole Trek portfolio for multi-role apps where session and role design matter as much as the protocol choice. For related reading, see essential Laravel packages, web development services, and ongoing support after go-live.

Frequently Asked Questions

SAML 2.0 sends signed XML assertions through browser POST or redirect flows for enterprise IdPs. OpenID Connect adds identity on OAuth 2.0 and returns JWT ID tokens, often with access tokens, suited to SPAs, mobile apps, and APIs.

In SP-initiated login, the user clicks sign in at your app. Your service provider sends an AuthnRequest to the identity provider. After the user authenticates, the IdP POSTs a SAML Response to your Assertion Consumer Service URL. Your server must verify the XML signature against IdP metadata, check NotBefore and NotOnOrAfter, confirm Audience matches your entity ID, and match InResponseTo when you sent the request. Extract NameID and attributes, map them to a local user, and start a session. Never trust a response without cryptographic validation.

OIDC runs on OAuth 2.0. The client requests authorisation, the user logs in at the IdP, and your app exchanges the code at the token endpoint—authorisation code with PKCE is the standard pattern for public clients. Discovery at /.well-known/openid-configuration exposes endpoints and scopes without manual XML. The IdP returns a signed JWT ID token proving identity and optionally an access token for API calls. Validate signature via JWKS, then check iss, aud, and exp before mapping sub as the stable external identifier.

Choose SAML when the customer IdP catalogue is SAML-only, you integrate with legacy ADFS, or procurement specifies SAML 2.0. Choose OIDC for SPAs, mobile apps, Laravel APIs with Sanctum or Passport, or when you need refresh tokens and scopes. Support both for B2B SaaS selling to mixed enterprises.

OIDC is the default for new cloud and developer-centric integrations, but SAML is not dead. Large enterprises standardised on SAML for years, and many IdPs still support both. New SaaS products often ship OIDC first and add SAML later for enterprise sales cycles where IT expects metadata XML and ACS configuration in Azure or Okta.

Yes. B2B platforms frequently expose a SAML connector for corporate IdPs and OIDC for Google, Microsoft, or partner APIs. Keep user records linked by a stable external ID per provider and document which tenant uses which protocol. On enterprise application builds, separate SAML and OIDC connectors are a common pattern when customers arrive with different IdP requirements.

Neither protocol is inherently more secure. Outcomes depend on TLS everywhere, correct signature validation, PKCE for public OIDC clients, short token lifetimes, and secret rotation. SAML’s XML complexity raises misconfiguration risk; OIDC’s JWT ecosystem is easier to audit with standard libraries when iss, aud, and exp checks are enforced server-side.

No. Sanctum issues tokens for your application’s API after a user is already authenticated. OIDC federates login to an external IdP. The typical pattern is OIDC or SAML for federated login, then Sanctum or Passport for SPA API sessions on your domain. Do not expose upstream IdP access tokens to browser clients unless you accept token passthrough risk.

Laravel does not ship OIDC in core. Use Laravel Socialite with a provider-specific driver such as socialiteproviders/microsoft-azure on Laravel 12 or 13 with PHP 8.3+. Redirect with openid, profile, and email scopes, handle the callback, and updateOrCreate users by external_id from getId(). Pair OIDC login with Sanctum or Passport for first-party API tokens rather than passing IdP tokens to the frontend.

Use a dedicated package such as aacotroneo/laravel-saml2 or 24slides/laravel-saml2. Configure IdP entity ID, SSO URL, and x509 cert plus SP certificates in config/saml2 and .env. Publish SP metadata for the IdP admin, wire ACS routes, and map attributes in an event listener after SignedIn—for example Azure’s emailaddress claim URI. Test with a dummy IdP before involving client IT and keep metadata in version control to diff certificate rotations.

Repeated failures include ACS or redirect URI mismatches from trailing slashes or http versus https, certificate expiry without monitoring, attribute mapping assumptions when Azure and Okta use different claim keys, confusing authentication with authorisation so SSO groups are not mapped to local roles, using OIDC implicit flow instead of authorisation code with PKCE, ignoring inconsistent single logout behaviour, and passing IdP tokens to the frontend. Rate-limit login and callback routes because federated endpoints attract abuse similar to password forms.

Fetch and cache IdP metadata XML with entity ID, SSO URL, and signing certificate. Confirm your SP entity ID and ACS URL match exactly what IT registered. Parse the SAML Response, verify the XML signature with the IdP X.509 cert, check NotBefore and NotOnOrAfter, confirm Audience restriction matches your entity ID, and match InResponseTo when you sent an AuthnRequest. Log assertion IDs to prevent replay if your library supports it. Keep NTP synced on Ubuntu production hosts because clock skew breaks NotOnOrAfter checks silently.

Fetch JWKS from the IdP and verify the JWT signature before decoding claims. Reject tokens with wrong iss or aud, expired exp, or excessive clock skew. Use nonce in implicit-like flows; with authorisation code plus PKCE, nonce is still recommended for hybrid setups. Map sub as the stable external identifier and treat email as authoritative only when the IdP marks it verified. Never log full tokens in production.

SAML flows are almost always browser-based using HTTP-POST and HTTP-Redirect bindings, which do not map cleanly to native mobile UX. Single Logout is often partial and brittle outside a full browser session. Unless you wrap SAML in a WebView, users get awkward redirects and unreliable global logout. OIDC with authorisation code and PKCE is the practical choice for mobile clients and API-first stacks where JSON tokens and refresh patterns fit better.

Federated login proves identity, not permission to billing or admin modules. Regenerate session ID on login, set secure HttpOnly SameSite cookies, and enforce authorisation with Laravel policies and Spatie Permission independent of IdP group claims. Store IdP client secrets and SAML private keys outside git using .env and encrypted env on Deployer releases—the pattern used on GitLab CI pipelines. Monitor IdP and SP certificate expiry because it is the most common production SSO outage across long-running client portal projects.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: