Single Sign-On (SSO) lets users log in once and access multiple applications without re-entering credentials. If you've ever clicked "Sign in with Google" or logged into your company's dashboard and had access to Slack, Jira, and Gmail automatically — that's SSO in action. Two protocols dominate the SSO landscape: SAML 2.0 and OpenID Connect (OIDC).
How SSO Works (The Big Picture)
Regardless of the protocol, SSO follows a common pattern:
- Identity Provider (IdP): The central authority that authenticates users (e.g., Okta, Azure AD, Auth0, Google Workspace).
- Service Provider (SP) / Relying Party (RP): The application the user wants to access (your app).
- Trust Relationship: The SP and IdP have a pre-configured trust — they've exchanged certificates or secrets ahead of time.
The user visits your app, gets redirected to the IdP, authenticates, and gets sent back with proof of identity. Your app trusts this proof because it trusts the IdP.
SAML 2.0 — The Enterprise Veteran
SAML (Security Assertion Markup Language) has been the backbone of enterprise SSO since 2005. It uses XML-based assertions passed between the IdP and SP.
SAML Authentication Flow
1. User visits https://app.example.com (Service Provider)
2. SP generates a SAML AuthnRequest (XML)
3. User's browser is redirected to the IdP with the AuthnRequest
4. IdP authenticates the user (login page, MFA, etc.)
5. IdP generates a SAML Response containing an Assertion
- The Assertion includes: user identity, attributes, conditions
- The entire Response is digitally signed with IdP's private key
6. User's browser POSTs the SAML Response back to the SP's ACS URL
7. SP validates the signature, checks conditions, extracts user info
8. SP creates a session — user is logged in
SAML Response Structure
<saml2p:Response>
<saml2:Assertion>
<saml2:Issuer>https://idp.example.com</saml2:Issuer>
<ds:Signature>...digital signature...</ds:Signature>
<saml2:Subject>
<saml2:NameID>user@example.com</saml2:NameID>
</saml2:Subject>
<saml2:Conditions NotBefore="..." NotOnOrAfter="...">
<saml2:AudienceRestriction>
<saml2:Audience>https://app.example.com</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AttributeStatement>
<saml2:Attribute Name="email">
<saml2:AttributeValue>user@example.com</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="role">
<saml2:AttributeValue>admin</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
Implementing SAML SP in Python
# Using python3-saml
from onelogin.saml2.auth import OneLogin_Saml2_Auth
def saml_login(request):
auth = OneLogin_Saml2_Auth(request, custom_base_path=settings.SAML_FOLDER)
return redirect(auth.login())
def saml_acs(request):
"""Assertion Consumer Service — receives the SAML Response"""
auth = OneLogin_Saml2_Auth(request, custom_base_path=settings.SAML_FOLDER)
auth.process_response()
errors = auth.get_errors()
if not errors:
user_data = {
'email': auth.get_nameid(),
'attributes': auth.get_attributes(),
'session_index': auth.get_session_index(),
}
# Create/update user and establish session
create_session(user_data)
return redirect('/dashboard')
else:
return HttpResponse(f'SAML Error: {errors}', status=400)
OpenID Connect (OIDC) — The Modern Standard
OIDC is built on top of OAuth 2.0 and uses JSON/JWT instead of XML. It was designed in 2014 as a simpler, more developer-friendly alternative to SAML.
OIDC Authorization Code Flow
1. User visits https://app.example.com
2. App redirects to IdP's authorization endpoint:
GET https://idp.example.com/authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://app.example.com/callback
&scope=openid profile email
&state=random_csrf_token
&nonce=random_nonce
3. User authenticates at the IdP
4. IdP redirects back with an authorization code:
GET https://app.example.com/callback?code=AUTH_CODE&state=random_csrf_token
5. App exchanges the code for tokens (server-to-server):
POST https://idp.example.com/token
{
"grant_type": "authorization_code",
"code": "AUTH_CODE",
"redirect_uri": "https://app.example.com/callback",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}
6. IdP returns tokens:
{
"access_token": "eyJ...",
"id_token": "eyJ...", // Contains user identity
"refresh_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}
7. App validates the id_token JWT and extracts user info
The ID Token
The key differentiator of OIDC is the ID Token — a JWT containing the authenticated user's identity:
// Decoded ID Token payload
{
"iss": "https://idp.example.com",
"sub": "user-uuid-12345",
"aud": "YOUR_CLIENT_ID",
"exp": 1712345678,
"iat": 1712342078,
"nonce": "random_nonce",
"email": "user@example.com",
"name": "Jane Developer",
"picture": "https://example.com/photo.jpg",
"email_verified": true
}
SAML vs OIDC — When to Use Which
- Use SAML when: Integrating with enterprise IdPs (Okta, Azure AD, ADFS), legacy systems require it, or your customers' IT teams expect SAML support. Most enterprise B2B SaaS products need SAML.
- Use OIDC when: Building modern web/mobile apps, using social login (Google, GitHub, Apple), building consumer-facing products, or when you want simpler implementation with JWTs.
- Support both when: Building a B2B SaaS product that serves both enterprise and smaller customers. Most identity platforms (Auth0, Okta) can act as a bridge, accepting SAML from enterprise IdPs and exposing OIDC to your app.
Key Differences at a Glance
Feature SAML 2.0 OIDC
────────────────── ────────────────── ──────────────────
Data Format XML JSON / JWT
Transport HTTP POST/Redirect HTTP GET/POST
Token Type XML Assertion JWT (ID Token)
Year Introduced 2005 2014
Best For Enterprise SSO Modern apps, mobile
Complexity High Medium
Mobile Support Poor Excellent
Discovery Manual config .well-known endpoint
Standard Body OASIS OpenID Foundation
Security Best Practices for Both
- Always validate signatures: Never trust assertions or tokens without verifying the cryptographic signature.
- Check timestamps: Validate
NotBefore,NotOnOrAfter(SAML) andexp,iat(OIDC) to prevent replay attacks. - Verify audience: Ensure the assertion/token was intended for your application.
- Use HTTPS everywhere: Tokens and assertions must only travel over TLS.
- Implement proper session management: Support single logout (SLO) and session timeouts.
- Store secrets securely: Client secrets and private keys belong in vaults, not config files.
SSO is no longer optional for serious applications. Whether you choose SAML, OIDC, or both, understanding these protocols deeply will help you build secure, user-friendly authentication that scales with your product.