Cloud Explainers

SAML vs OAuth vs OpenID Connect: What's the Difference?

Auth (OAuth, JWT)9 min videoPublished Nov 10, 2026

SAML, OAuth and OpenID Connect are usually listed together, as if they were three ways to log in. They aren't: two of them say who you are, and one only says what an app may do. We built one sign-in service with all three in a lab and followed a receptionist's morning through them: a SAML sign-in to a payroll site, an OAuth permission for a reminder app, and the one extra word that turns OAuth into OpenID Connect.

Three names, one login?

You're setting up login, and the page asks you to pick: SAML, OAuth, or OpenID Connect. They look like three ways to log a person in. They aren't. One of them never tells the app who you are.

Three ways to log you in?: diagram of SAML, OAuth, OpenID Connect

Here's a real app, right after its user clicked Yes on an OAuth screen. It was handed this: a token, five minutes of life, one permission. No name. No email. So how does a Sign in with Google button know who you are? One extra word. We'll find it.

remindly: the reminder app's server
jq '{token_type, expires_in, scope, id_token}' oauth_tokens.json{
  "token_type": "Bearer",
  "expires_in": 300,
  "scope": "calendar.read",
  "id_token": null
}

Two different questions

Start with two questions that sound alike. Authentication: who is this person? Authorization: what is this app allowed to do? At a concert door, those are two different papers. Your photo ID says who you are. Your ticket says what you may do: this show, tonight, for whoever is holding it. SAML and OpenID Connect are photo IDs. OAuth is a ticket.

Two questions at a concert door: diagram of Who is this person?, A photo ID, What may this app do?, A ticket, SAML, OpenID Connect, OAuth

To prove it, we built all three around one made-up dental clinic. Ana, the receptionist, has one account, at Maple ID: our stand-in for a Google or Microsoft account. This morning she'll use it three ways.

Ana's morning at the dental clinic: diagram of Ana, Maple ID, Payroll, Remindly, Calendar

SAML: who you are, in XML

First, payroll. Ana opens the site, and it has no password for her. It sends her browser to Maple ID instead. Look at the address: this is Maple ID's own page. She types her password here, and Payroll never sees it. One click later she's back at Payroll: Welcome, Ana Reyes.

SAML: who you are, in XML: browser screenshot of where Payroll sent her, Maple ID's page, not Payroll's, typed here only

Here's what happened in between. Payroll sent the browser to Maple ID with a request. Ana proved who she is. Then Maple ID wrote a short document about her, signed it, and handed it to the browser, which posted it to Payroll. That document is called a SAML assertion. During the sign-in, the two servers never talk to each other: the browser carries everything.

A SAML sign-in: the browser carries it: diagram of Payroll, Browser, Maple ID, Open Payroll, Go ask Maple ID, Ana signs in, Signed document, Posts it to Payroll, The two servers never talk during the sign-in

And here it is, as Payroll read it. Who wrote it: Maple ID. Who it's about: Ana, by her email address. Who it's for: Payroll, and no one else. It's valid for sixty seconds. And it carries Maple ID's signature, a seal Payroll can check and nobody else can fake. It's written in XML, an older text format: about four and a half thousand characters of it.

payroll: the payroll site's server
saml-showformat:      XML, 4,466 characters
Issuer:      https://id.maple.test/realms/maple
NameID:      ana@clinic.test
firstName:   Ana
lastName:    Reyes
Audience:    https://payroll.test/saml
NotBefore:   2026-11-10T15:27:56.713Z
NotOnOrAfter: 2026-11-10T15:28:56.713Z
Signature:   valid, rsa-sha256

That signature works like a tamper seal. Run Payroll's check on a copy with one detail changed, a different email address, and it's refused. So that's SAML, a standard since two thousand five. It tells a site who you are. It says nothing about what an app may do for you.

payroll: the payroll site's server
saml-checksignature valid: NameID ana@clinic.test
payroll: the payroll site's server
saml-check --change ana@clinic.test dr.lee@clinic.testcopy: 1 place(s) changed, ana@clinic.test -> dr.lee@clinic.test
REFUSED: signature check failed (InvalidDigest)

OAuth: what an app may do

Now the reminder app. Remindly wants to read the clinic's calendar, so it can text patients before each appointment. Ana could give it her Maple password, but then it could open everything she can. OAuth, from two thousand twelve, gives it a ticket instead: one permission, for a short time, and no password ever leaves Maple ID.

How does Remindly get into the calendar?: diagram of Remindly, Ana's password, Her whole account, An OAuth ticket, The calendar

Ana clicks Connect calendar, and Maple ID asks her one thing: may Remindly view your calendar? Notice what it didn't ask for: her password. She typed it a minute ago, for Payroll, and Maple ID remembers. That's single sign-on. She clicks Yes, and Remindly shows today's three appointments.

OAuth: what an app may do: browser screenshot of a permission question, the one permission, no password this time, she can still say no

This trip differs from SAML in one way that matters. When Ana clicks Yes, her browser doesn't carry the ticket. It carries only a one-time code back to Remindly. Remindly then calls Maple ID directly, server to server, and swaps that code for the access token. So the token never sits in the browser, where it could leak.

An OAuth trip: the browser carries only a code: diagram of Remindly, Browser, Maple ID, Go ask Maple ID, Ana clicks Yes, A one-time code, Hands over the code, Code + Remindly's secret, The access token

That access token is the ticket. Remindly shows it to the calendar API, and gets the appointments. Then it tries to write, to add one, with the same token. The API answers four oh three, forbidden: insufficient scope. Scope is OAuth's word for permission, and this ticket says read. Notice what the API never asked: who Ana is.

remindly: the reminder app's server
calendar-api GET /eventsGET https://calendar.maple.test/events
Authorization: Bearer eyJhbGciOiJS... (the access token)
-> 200
   09:00  Check-up, room 1
   10:30  Filling, room 2
   14:00  Cleaning, room 1
remindly: the reminder app's server
calendar-api POST /eventsPOST https://calendar.maple.test/events
Authorization: Bearer eyJhbGciOiJS... (the access token)
-> 403
{
 "error": "insufficient_scope",
 "error_description": "this request needs the scope calendar.write"
}

Open the ticket and you can see why. It names the one place it works: the calendar API. It names the one permission. It lasts five minutes, because whoever holds a ticket can use it. And for the user, there's only an account number: no name, no email.

Inside Remindly's access token (decoded from the real one): cards of Works at, Allows, Lasts, The user

OpenID Connect: one more word

So how did Sign in with buttons ever work? For years, each app improvised: get a ticket, use it to fetch the account's profile, and trust whatever name came back. Every provider did it differently, and it wasn't always safe: a ticket proves that someone allowed something, not who is at the keyboard.

Before 2014: login, improvised on top of OAuth: diagram of An app, A ticket, The profile, A name comes back

In two thousand fourteen, OpenID Connect made it one standard. It is OAuth, the very same trip, with one extra word in the request: openid. That word means: also tell me who this is. The words after it say which details. It's what Google uses: its documentation says its sign-in conforms to OpenID Connect.

What Remindly asks Maple ID for, to sign Ana in: diagram of scope=openid profile email, scope= = what the app asks for, openid = also: who is this?, profile email = which details

Ana clicks Sign in with Maple ID. Same trip, but now the question lists her profile and her email address. Yes. And Remindly greets her by name.

OpenID Connect: one more word: browser screenshot of who she is, not what to open

What changed in Maple ID's answer? One new item: an ID token. Decoded, it reads a lot like that SAML document. Who wrote it: Maple ID. Who it's for: Remindly. Who it's about: Ana Reyes, with her email. And when it expires. It's signed, too. The same photo ID, in JSON instead of XML, at about a quarter of the size.

remindly: the reminder app's server
jq '{scope, id_token: .id_token[0:20]}' oidc_tokens.json{
  "scope": "openid profile email",
  "id_token": "eyJhbGciOiJSUzI1NiIs"
}
remindly: the reminder app's server
jq '{iss, aud, sub, name, email, exp}' id_token.json{
  "iss": "https://id.maple.test/realms/maple",
  "aud": "remindly",
  "sub": "51da1aa3-59df-4808-b2c7-c6df03b8def8",
  "name": "Ana Reyes",
  "email": "ana@clinic.test",
  "exp": 1794324781
}

Remindly now holds two tokens from Maple ID, both real, both signed. Suppose it shows the calendar API the ID token instead. Take a guess: does the API accept it? No. Four oh one, unauthorized: this token was written for Remindly, not for the calendar. A photo ID doesn't get you into the show, and a ticket doesn't prove who you are. The ID token is for the app. The access token is for the API.

remindly: the reminder app's server
calendar-api GET /events --token idGET https://calendar.maple.test/events
Authorization: Bearer eyJhbGciOiJS... (the ID token)
-> 401
{
 "error": "invalid_token",
 "error_description": "this token was written for remindly, not for https://calendar.maple.test"
}

Which one do you need?

So two of the three do the same job, a generation apart. SAML: two thousand five, XML, made for a person at a browser, and everywhere in company single sign-on. OpenID Connect: two thousand fourteen, JSON, built on OAuth and plain web requests, so it's at home in mobile apps too.

SAML and OpenID Connect: the same job: comparison table of SAML vs OpenID Connect, Tells a site, A standard since, Written in, At home in, Built on

Three words you'll meet along the way. Identity provider: the service that knows your password, like our Maple ID. Single sign-on: sign in there once, and every site that trusts it lets you in. And JWT, said jot: not a fourth choice, just a format. A small piece of signed JSON. The ID token is one.

Three words you'll meet: cards of Identity provider (IdP), Single sign-on (SSO), JWT (JSON Web Token), Our ID token is one

Which one do you need? Adding login to a new app: OpenID Connect. Your app has to call an API on a user's behalf: that's OAuth, and OpenID Connect already rides on it. A company customer says, connect to our identity provider, or the app you bought only speaks SAML: then SAML. And if you keep one line: OAuth is a ticket. SAML and OpenID Connect are IDs.

Which one do you need?: cards of Login for a new app, Call an API for a user, A company's single sign-on, To remember

Recap

So: one sign-in service, three things it hands out. SAML: a signed XML document that says who you are. OAuth: a ticket for one thing an app may do. OpenID Connect: that ticket, plus a signed ID. Now you know which question each one answers.

One sign-in service, three things it hands out: diagram of Maple ID, SAML assertion, Access token, ID token, Payroll, Calendar API, Remindly, OpenID Connect = OAuth + an ID

Every command and the lab files are linked in the description, so you can run all three sign-ins yourself. If this untangled three names you kept seeing together, subscribing means the next one finds you.

Every page and every refusal here came from a real run in our lab. Thanks for watching Cloud Explainers.

Sources

Demos: containers on our own Mac standing in for a sign-in service, a payroll site, a reminder app and a calendar API; the clinic and the products are made up. Nothing was attacked: every refusal is a service checking its own input. Watch the full video on YouTube.

More videos

Linux9 min

Linux Makefiles Will Finally Make Sense After This

Python7 min

How to Fix Python cron Jobs That Never Run

Linux22 min

Linux Bash Scripting: Full Tutorial for Beginners

Networking10 min

How PXE Network Boot Works

Active Directory9 min

How Active Directory Works

Linux10 min

How to Fix a Linux systemd Service That Won't Start

Linux9 min

Linux Logs: syslog vs journald

Auth (OAuth, JWT)9 min

JWT vs Session Cookies: Which One Should You Use?

Docker10 min

Docker vs Docker Compose: Which One Should You Use?

Docker9 min

How to Fix Docker Compose Containers That Can't Talk to Each Other

Django (Python web framework)22 min

Django Full Tutorial for Beginners

Linux9 min

How Linux systemd Works

Networking8 min

How to Fix 'Windows Cannot Access' Network Share Errors (SMB)

APIs10 min

REST vs gRPC: Which One Should You Use?

Certificates & PKI (ADCS)9 min

How to Fix SSL Certificate Chain Errors (Missing Intermediate)

Python9 min

Every Python Concept for Beginners in 10 Minutes

Databases & SQL9 min

Every SQL Join Type in 10 Minutes

OpenRouter (one API for many AI models)9 min

Why OpenRouter Free Models Hit Their Limit So Fast

Gemini API & Google AI Studio9 min

Why Your Gemini API Free Tier Runs Out So Fast

OpenRouter (one API for many AI models)10 min

OpenRouter Will Finally Make Sense After This

Caching & Redis10 min

How Redis Caching Works

Gemini API & Google AI Studio10 min

Gemini API vs Vertex AI: Which One Should You Use?

Gemini API & Google AI Studio8 min

How to Fix a Leaked Gemini API Key

Go (Golang)9 min

Why Golang Uses Goroutines Instead of Threads

Gemini API & Google AI Studio9 min

How to Fix Gemini API Error 429: Resource Exhausted

Claude & the Claude Developer Platform10 min

Claude Token Pricing Will Finally Make Sense After This

Python11 min

Why Python Web Scraping Returns Nothing (and the Fix)

Python10 min

Python uv vs pip: Which One Should You Use?

Python22 min

Python Automation: Full Tutorial for Beginners

Auth (OAuth, JWT)9 min

How OAuth Login Works

Gemini API & Google AI Studio10 min

Google AI Studio Will Finally Make Sense After This

Docker10 min

What Happens When You Run a Docker Container

Claude & the Claude Developer Platform8 min

How to Fix Claude Code Billing Your API Key Instead of Your Plan

Auth (OAuth, JWT)9 min

Why Passkeys Can't Be Phished Like Passwords

PowerShell10 min

PowerShell vs Bash: Which One Should You Learn?

Linux10 min

The Linux Roadmap for Beginners

Django (Python web framework)9 min

Django vs FastAPI: Which One Should You Use?

Databases & SQL10 min

Why SQLite Is the Most Used Database in the World

Databases & SQL11 min

How a NoSQL Database Like MongoDB Works

Python11 min

How to Fix Python pip error: externally-managed-environment

Databases & SQL9 min

How to Fix MySQL Too Many Connections Database Errors

Go (Golang)9 min

Golang Concurrency Will Finally Make Sense After This

AWS10 min

How Amazon S3 Works

Kubernetes10 min

How Kubernetes Works

Git10 min

How Git Works

Docker10 min

How to Fix Docker Port Already Allocated Errors

PostgreSQL10 min

How PostgreSQL Works

Git8 min

How to Fix Git Merge Conflicts

Linux9 min

How to Fix No Space Left on Device in Linux

Networking10 min

How the Internet Works

Databases & SQL9 min

SQL vs NoSQL: When Each One Wins

Auth (OAuth, JWT)8 min

How to Fix JWT Token Expired Errors (401 Unauthorized)

Claude & the Claude Developer Platform9 min

Claude API vs Claude Pro: Why Your Subscription Doesn't Include It

AI agent skills9 min

Why AI Agent Skills Beat Giant Prompts

Linux10 min

Linux Cron Jobs Will Finally Make Sense After This

Cybersecurity10 min

Security Logs Will Finally Make Sense After This

Cybersecurity8 min

How to Fix a Security Leak: An API Key Pushed to GitHub

DNS9 min

DNS Will Finally Make Sense After This

Cybersecurity9 min

Never Ignore a Security Update

Auth (OAuth, JWT)9 min

Two-Factor Authentication Will Finally Make Sense

Networking8 min

Never Expose a Database to the Internet

Cybersecurity10 min

SSH Keys vs Passwords: Which Is Safer for Security?

Linux10 min

The 5 Levels of Linux Boot: From Power On to Login

Networking12 min

The 5 Levels of Network Firewalls

Cybersecurity10 min

Password Security: Why Hackers Crack Weak Passwords

Linux9 min

Why Your Linux Server Runs Out of Memory

Networking10 min

HTTP vs HTTPS: What Actually Changes on Your Network

Linux12 min

Every Linux File Permission You Need to Know

Linux10 min

Linux Processes vs Threads: When Each One Wins

Networking11 min

The OSI Model Will Finally Make Sense After This

Git11 min

The 7 Levels of Git

Networking10 min

Every Network Troubleshooting Command You Need

Cybersecurity10 min

Hashing vs Encryption in Cybersecurity: When Each One Wins

Networking11 min

Why the Internet Ran Out of IP Addresses

Linux11 min

Linux Package Managers: apt vs Snap vs Flatpak

Python9 min

Why Python Is Slow (and When It Doesn't Matter)

Networking11 min

TCP vs UDP: When Each One Wins

Linux8 min

Stop Running Linux as Root (Do This Instead)

Linux11 min

Linux Users and Groups Will Finally Make Sense

Cybersecurity9 min

Why Phishing Beats Good Security (and How to Spot It)

Docker12 min

The 7 Levels of Docker

AI agents & MCP12 min

AI Agents Will Finally Make Sense After This

Git8 min

Stop Using git push --force (Use This Instead)

AI agent skills10 min

Agent Skills vs MCP: When Each One Wins

Networking12 min

Networking Will Finally Make Sense After This

Linux12 min

Every Linux Command You Actually Need

Cybersecurity12 min

The 7 Levels of Cybersecurity

System design12 min

Every System Design Concept You Need, in One Diagram

PostgreSQL10 min

Why PostgreSQL Ignores Your Index