What Is OpenID Connect? OIDC vs OAuth Explained
I shipped a "Sign in with Google" button once and told my team it was OAuth. A reviewer left exactly one comment on the pull request: "Where are you validating the ID token?" I didn't have one. I was reading the user's email out of an access token I wasn't even verifying, and calling that a login. That comment was my real introduction to OpenID Connect โ and to a distinction that a surprising number of shipped auth flows get wrong.
So let me save you the code-review embarrassment and walk the difference from the gap it exists to fill.
The gap OAuth leaves open
OAuth 2.0 is a delegation framework. Its whole job is to let an app do something on your behalf โ read your calendar, post to your feed โ without you handing over your password. If that's fuzzy, what is OAuth covers the four roles and the token dance in full. The important thing here is what OAuth deliberately does not do: it never tells the app who you are. It grants a scoped, expiring access token that says what the app may do, and stops there.
That's a feature, not an oversight. "Let this app read my photos" genuinely doesn't require the app to know your name. But it leaves a hole the moment you want to log in with an existing account, because now identity is the entire point โ and OAuth alone won't give it to you in any trustworthy form. (In the enterprise world the older answer to that same need is SAML; SAML vs OIDC compares the two federated-login approaches head to head.)
The shortcut that quietly breaks
Here's the trap I fell into, and it's an easy one. After an OAuth flow you're holding an access token, and many providers let you call a userinfo-style endpoint with it to fetch the account's email and name. So you do that, read the email, and treat the user as logged in. It even works in the demo.
The problem is subtle: an access token is a capability, not a statement of identity. It's designed to be handed to APIs, it can be issued to a different application than yours, and depending on the flow you may have no cryptographic guarantee about who it really belongs to or which app it was minted for. Building login on top of it is like accepting a valet parking ticket as someone's passport โ it's the wrong document for the question you're asking. This class of confusion is a well-known source of authentication bugs.
What OIDC adds: the ID token
OpenID Connect closes that hole with one clean addition: the ID token. When you request login (by adding the openid scope to an otherwise ordinary OAuth authorization-code flow), the provider returns, alongside the access token, a second token whose entire purpose is to say who authenticated. It's a JSON Web Token โ a signed JWT โ and being signed is the point: your app can verify it came from the provider and hasn't been altered, without a network call back to ask.
That's the OIDC bargain in one line. OAuth already knew how to hand out tokens after a user approves an app; OIDC just defines one more token, with a standard shape, that answers the identity question OAuth left on the table. The "Sign in with Google" feeling everyone attributes to OAuth is really this โ a thin identity standard riding on OAuth's rails.
What's actually inside an ID token
Decode an ID token and you'll find a predictable set of claims, each doing a validation job:
- sub โ the subject: a stable, unique ID for the user at that provider. This, not the email, is the thing you key your accounts on (emails change and get reassigned).
- iss โ the issuer: which provider minted the token. You check it matches the provider you actually integrated.
- aud โ the audience: the client ID this token was issued for. If it's not your app, reject it โ this is the check that stops a token meant for someone else being replayed at you.
- exp and iat โ expiry and issued-at timestamps, the same lifetime mechanics as when a JWT expires.
- nonce โ a value your app generated and sent into the flow, echoed back so you can confirm this token answers your login request and isn't a replay.
- Often email, name, and picture for a friendly profile โ convenient, but the trustworthy identifier is still
sub.
You can paste a real ID token into the JWT Decoder to see these claims laid out, and check its lifetime with the JWT Expiration Checker. Just remember decoding is not verifying โ the decoder shows you the contents, but your server still has to validate the signature and those claims before believing a word.
ID token vs access token: don't cross the streams
This is the single most useful thing to hold onto, because getting it wrong is what my reviewer caught:
- The ID token is for your app to read. It answers "who is this user," you validate it once at login, and you generally don't send it anywhere else.
- The access token is for a resource server to receive. It answers "what may this app do," and it rides to APIs in the
Authorization: Bearerheader โ the Bearer Token Parser is what pulls it back out of that header on the receiving side.
Send an ID token to an API as a credential, or trust an access token as proof of identity, and you've mixed up two documents with different audiences and different jobs. Different from either is a static API key โ an app-level secret with no user identity attached at all, generated with an API Key Generator โ which is worth contrasting so you don't reach for the wrong credential model entirely.
Validate before you trust
The protocol is only as safe as your validation, so the checklist is the security: verify the ID token's signature against the provider's published keys, confirm iss is the provider you expect, confirm aud is your client ID, confirm it hasn't expired, and confirm the nonce matches the one you sent. Every OIDC-certified library does this for you โ the right move is to use one, not to hand-roll JWT parsing and skip a step. My original bug wasn't a flaw in OpenID Connect; it was me never running these checks because I didn't know the ID token existed.
The takeaway
OpenID Connect is the identity layer OAuth doesn't include: a small standard that adds a signed ID token so an app learns who the user is, in a form it can verify, while still using OAuth to handle what the app may do. Keep the two tokens in their lanes โ ID token for your app to validate at login, access token for APIs โ validate every ID token's signature and claims before trusting it, and key users on sub, not email. Get that straight and "Sign in with Google" stops being magic and becomes a checklist you can actually secure.
Try the tools
Frequently Asked Questions
What is the difference between OAuth and OpenID Connect?
OAuth 2.0 is an authorization framework: it lets an app get scoped, delegated access to your data on another service. It never actually tells the app who you are. OpenID Connect is a small standard layered on top of OAuth that adds authentication โ it issues an ID token proving your identity in a verifiable form. Put simply: OAuth authorizes, OIDC identifies, and OIDC uses OAuth's machinery to do it.
What is an ID token?
An ID token is a signed JWT that an OpenID provider issues to prove who the user is. It carries claims like sub (a stable user ID), iss (which provider issued it), aud (which app it's for), exp (expiry), and often name and email. The app verifies the signature and those claims, then trusts the identity โ no password ever changes hands. It's meant to be read by the app, not sent to APIs.
Is OpenID Connect the same as OAuth 2.0?
No, but it's built on it. OIDC reuses OAuth's flows (like the authorization code flow), endpoints, and tokens, then adds three things: the ID token, a standardized userinfo endpoint, and a discovery document so clients can auto-configure. If OAuth is the engine, OIDC is the identity feature bolted onto it. You can run OAuth without OIDC, but you can't run OIDC without OAuth underneath.
Is OpenID Connect secure?
Yes, when implemented correctly. Its security rests on validating the ID token: check the signature against the provider's published keys, confirm iss and aud match what you expect, confirm the token hasn't expired, and check the nonce to defend against replay. The common failures aren't in the protocol โ they're apps that read claims from an unverified token or misuse an access token as proof of identity.
What is the difference between an ID token and an access token?
An ID token answers 'who is the user' and is meant for the client app to read and validate. An access token answers 'what is this app allowed to do' and is meant to be sent to a resource server (an API) as a bearer credential. They have different audiences and different jobs. Sending an ID token to an API, or trusting an access token as proof of identity, is a frequent and dangerous mix-up.
Ingrid Solberg writes for CodeUtilityKit, where the team builds free, privacy-first developer tools that run entirely in your browser. Every guide is written and reviewed by developers who use these tools daily.