12 October 2026 · Dean Hume
OAuth 2.0 vs OpenID Connect: What's the Difference and Which Do You Need?

Have you ever sat down to add "Sign in with Google" to an app, started reading about OAuth, and then half an hour later found yourself reading about something called OpenID Connect, wondering if you're in the right place?
The two get mentioned together so often that it's easy to assume they're the same thing, or that they're competing options. They're not, but picking the wrong one (or using one when you actually needed both) is one of the most common mistakes I see in real-world projects.
In my previous posts I walked through using OAuth 2.0 with C# and using OAuth 2.0 with Node.js. Both of those were about getting an access token so an app could call an API. This time, I want to step back and answer the question that comes up right afterwards: where does OpenID Connect fit in, and do you need it?
On this page
- The short answer
- A hotel analogy
- What OAuth 2.0 actually does
- What OpenID Connect adds
- Access tokens vs ID tokens
- What it looks like in practice
- Which one do you need?
- Common mistakes
- Conclusion
The short answer
- OAuth 2.0 is about authorization. It answers the question: "What is this app allowed to do?"
- OpenID Connect (OIDC) is about authentication. It answers the question: "Who is this user?"
OpenID Connect isn't a replacement for OAuth 2.0 - it's a thin layer that sits on top of it. OpenID Connect OIDC builds on OAuth 2.0 and adds an ID token, giving your app a standard way to find out who signed in.
If there is one takeaway from this post - its that.
A hotel analogy
I find it easiest to think about this with a hotel.
When you check in, the receptionist looks at your passport and confirms that you're the person who made the booking. That's authentication - proving who you are.
They then hand you a key card. That card doesn't have your name on it, and it doesn't prove who you are. It simply opens your room, the gym and the lift to your floor, and nothing else. That's authorization - what you're allowed to access.
- OAuth 2.0 is the key card.
- OpenID Connect is the check-in desk that tells the hotel (your app) who has just arrived.
A key card is perfect for opening doors. But if someone hands you a key card, you can't be certain whose it is.
What OAuth 2.0 actually does
OAuth 2.0 was designed to solve a specific problem: letting one application access resources on behalf of a user (or on behalf of itself) without sharing passwords.
Think of a photo printing service that wants to read your photos from Google Photos. Instead of asking for your Google password, it asks Google for permission. You approve it, and the service receives an access token that works for a limited time and a limited set of actions (the scopes).
What OAuth 2.0 gives you:
- An access token to call an API
- Scopes that limit what the token can do
- Optionally, a refresh token to get new access tokens without asking the user again
What it deliberately does not define:
- Who the user is
- How to read any information about them
- What format the access token should be in (it can be a JSON Web Token, or JWT, but it doesn't have to be)
A JWT is a compact format for carrying claims - pieces of information such as who issued the token or when it expires.
The missing piece is a standard way to answer "who just logged in?". That's what OIDC adds.
What OpenID Connect adds
OIDC fills that gap with a small set of standard additions:
- An ID token. A signed JWT that contains claims about the user and about the sign-in event itself.
- The
openidscope. Include this in your request and you're telling the provider "this is an OIDC request, please send me an ID token". - Standard scopes and claims. Scopes like
profileandemailmap to well-known claims such asnameandemail. - A UserInfo endpoint. An API you can call with an access token to get more details about the signed-in user.
- A discovery document. Providers that support discovery publish their settings at
/.well-known/openid-configuration, so libraries can configure themselves automatically.
That discovery document is a real time saver. Try opening this in your browser to see what one looks like:
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
You'll see the authorization endpoint, token endpoint, supported scopes, signing keys and more, all in one place.
Access tokens vs ID tokens
In the authorization code flow we're looking at here, you'll end up with both tokens, and they have very different jobs.
| Access token | ID token | |
|---|---|---|
| Defined by | OAuth 2.0 | OpenID Connect |
| Purpose | Call an API | Tell your app who signed in |
| Intended audience | The API (resource server) | Your app (the client) |
| Format | Anything - opaque string or JWT | Always a JWT |
| Who should read it | The API | Your app |
| Send it to an API? | Yes | No |
A decoded ID token payload looks something like this:
{
"iss": "https://login.microsoftonline.com/your-tenant-id/v2.0",
"sub": "AAAAAAAAAAAAAAAAAAAAAIkzqFVrSaSaFHy782bbtaQ",
"aud": "your-client-id",
"exp": 1760000000,
"iat": 1759996400,
"nonce": "a-random-value-your-app-generated",
"name": "Dean Hume",
"email": "[email protected]"
}
A few of those claims are worth knowing:
iss- who issued the tokensub- the unique, stable identifier for the user at that provideraud- who the token is for. For an ID token, this is your app's client IDexp- when it expiresnonce- a value your app sent in the original request, used to prevent replay attacks
The aud claim is a good way to remember the difference. An ID token is addressed to your app. An access token is addressed to the API. Neither should be forwarded to the wrong recipient.
What it looks like in practice
The difference between the two shows up in a single line of the request. Here's an authorization request using the authorization code flow, with PKCE (Proof Key for Code Exchange) to help stop someone who intercepts an authorization code from exchanging it for tokens:
GET https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/authorize
?client_id=your-client-id
&response_type=code
&redirect_uri=https://yourapp.com/callback
&scope=User.Read
&state=random-state-value
&code_challenge=your-pkce-challenge
&code_challenge_method=S256
That's plain OAuth 2.0. When the code is exchanged, you'll get an access token for User.Read, but no ID token.
Now add the openid scope (and a nonce, which OIDC requires for some flows):
GET https://login.microsoftonline.com/your-tenant-id/oauth2/v2.0/authorize
?client_id=your-client-id
&response_type=code
&redirect_uri=https://yourapp.com/callback
&scope=openid profile email User.Read
&state=random-state-value
&nonce=random-nonce-value
&code_challenge=your-pkce-challenge
&code_challenge_method=S256
Now it's an OpenID Connect request. The token response will include an id_token alongside the access_token:
{
"token_type": "Bearer",
"scope": "openid profile email User.Read",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1Qi...",
"id_token": "eyJ0eXAiOiJKV1Qi..."
}
The request looks almost identical. The important difference is what your app does with the ID token afterwards. In practice you'll rarely build these requests by hand - libraries like MSAL, openid-client (Node.js) or ASP.NET Core's built-in OpenID Connect handler do it for you - but it helps to know what's happening underneath.
Also worth noting: the client credentials flow I used in my earlier posts has no user involved, so there's nothing for OIDC to say. A background service calling an API as itself needs OAuth 2.0 only.
Which one do you need?
Here's the rule of thumb I use.
You need OAuth 2.0 only when:
- A backend service or script calls an API as itself (machine-to-machine)
- Your app needs to call an API on a user's behalf, and you don't need to know who the user is
- You're protecting your own API and validating access tokens
You need OpenID Connect when:
- Users need to sign in to your app
- You want "Sign in with Google / Microsoft"
- You need reliable information about the user, such as their name or email
- You want single sign-on (SSO) across several apps
You need both when:
- Users sign in to your app and your app calls APIs for them. This is the most common setup for web and mobile apps, and it's exactly what the request above does:
openid profile emailfor the sign-in,User.Readfor the API.
If you're not sure, ask yourself this: "Does my app need to know who the user is?" If the answer is yes, you need OIDC.
Common mistakes
These are the ones I see most often.
1. Using an access token as proof of login
This is the big one. Receiving an access token only tells you that a user authorized something. It doesn't reliably tell your app who they are, or that they just signed in to your app. Using OAuth alone for login is sometimes called "pseudo-authentication", and it has led to real security issues. If you need login, use OIDC and validate the ID token.
2. Sending the ID token to an API
The ID token is for your app, not your API. APIs should accept access tokens, and check that the aud claim matches them. Passing an ID token to an API works on some systems purely by accident, and it will bite you later.
3. Not validating the ID token
Your app needs to check more than that it "looks like a JWT". At a minimum, validate:
- The signature, using the provider's published keys
- The issuer (
iss) - The audience (
aud) matches your client ID - The token hasn't expired (
exp) - If you sent a nonce, check that the returned value matches
The good news is that well-maintained libraries do all of this for you. Please don't write your own.
4. Using email as the user's unique ID
Emails change, and some providers let users have several. The sub claim is the stable identifier, so use iss + sub together as the key in your database.
5. Reading access tokens in your client app
Even when an access token is a JWT, it's meant for the API. The format can change without warning, and some providers issue opaque tokens that you can't read at all. If your app needs user details, read the ID token or call the UserInfo endpoint.
Conclusion
- Need to call an API? OAuth 2.0.
- Need to know who the user is? OpenID Connect.
- Building an app where users sign in and the app calls APIs? Both.
If you'd like to see the OAuth 2.0 side in code, take a look at my guides for C# and Node.js. I'm also planning to cover the authorization code flow with PKCE in a future post.


