PropelAuth Logo
Back to Blog

What is OIDC? OpenID Connect Explained (and How It Compares to SAML)

What is OIDC? OpenID Connect Explained (and How It Compares to SAML)

OIDC (OpenID Connect) lets your app send a user somewhere else to log in, like Google or Okta, and get back a signed answer to one question: who is this person?

It's the protocol behind nearly every "Sign in with Google" button. It's also one of the two protocols an enterprise customer means when they say "we need SSO." The other is SAML.

OIDC in one picture: your app asks an identity provider like Google or Okta "who is this person?" and gets back a signed answer saying this is Alice, verified.

Most developers meet OIDC in two forms. The first is social login. When you add "Sign in with Google" to your app, a library handles OIDC for you, and you may never look at the protocol itself. The second is when a customer's IT admin asks whether you support "OIDC with Okta." Same protocol, but now the identity provider belongs to your customer, and you need to understand what's actually happening.

In this post we'll cover what OIDC is, walk through a login step by step, look at what's inside the token you get back, and compare it to OAuth 2.0 and SAML. By the end you should be able to tell a customer which one you support and why.

What Is OIDC?

Let's start off simple and we'll fill in the details after.

Imagine a bouncer who doesn't check IDs himself. When you walk up, he sends you next door to the DMV. The DMV checks your license and hands you a sealed note:

"This is Alice. We verified her. Good for the next hour. Signed, the DMV."

You bring the note back to the bouncer. He doesn't need to know anything about you ahead of time. He just needs to trust the DMV and recognize their signature.

That's OIDC. Your app is the bouncer (the spec calls it the relying party). The identity provider (the OpenID provider, or IdP) is the DMV. The sealed note is the ID token.

Now the precise version. OIDC is an open standard, published by the OpenID Foundation in 2014, that lets an application verify a user's identity by delegating the login to an identity provider. It isn't a standalone protocol. It's a thin identity layer on top of OAuth 2.0. It reuses OAuth's request-and-redirect flow and adds, above all, a token that says who the user is. That addition is the whole reason OIDC exists.

Wait, Isn't That Just OAuth?

Almost. OIDC is OAuth 2.0 plus one addition, and that addition is what turns an authorization protocol into a login protocol.

OAuth 2.0 was designed for authorization. It answers "is this app allowed to do X on the user's behalf?" Giving a scheduling tool read access to your Google Calendar is the textbook example. The tool gets an access token, which is a credential for calling Google's API on your behalf.

In practice, though, OAuth has been used for login for about as long as it has existed. "Sign in with Google" predates OIDC, and the way it worked was essentially this: get an access token, then use it to read the user's email address from Google. If Google says the token belongs to alice@acme.com, you log Alice in. That works, but every provider did it a little differently. The user-info endpoint, the field names, and the token format were all provider-specific, so each "Sign in with ___" button was its own integration.

OIDC standardizes the login part. It runs the same request and redirect as OAuth 2.0 but adds a scope called openid. In return, the IdP issues an ID token next to the access token. The ID token is always a JWT, and its claims (sub, email, name, and so on) mean the same thing at every provider. Instead of calling a provider-specific API to find out who logged in, your app verifies one token and reads the identity out of it.

OAuth 2.0OIDC
Designed forAuthorization: can this app access this resource?Authentication: who is this user?
Token you getAccess token (for calling APIs)ID token (for identifying the user), plus an access token
Token formatUp to the provider: an opaque string or a JWTThe ID token is always a JWT
How you learn who the user isCall the provider's user-info APIRead the standard claims in the ID token
DiscoveryNot standardized/.well-known/openid-configuration

Access tokens can be either opaque strings or JWTs, depending on the provider, and plenty of apps do look inside them. The point isn't that OAuth tokens are unreadable. It's that OIDC gives you a token whose format and meaning are the same everywhere, built specifically to answer "who is this?" If what you're building is login, use OIDC wherever the provider offers it.

How Does an OIDC Login Actually Work?

Let's make the actors concrete:

  • Your team builds the app.
  • Acme is your customer.
  • Alice works at Acme.
  • Okta is Acme's identity provider, set up by Acme's IT admin.

Alice opens your app and clicks "Sign in with SSO."

For OIDC authentication in a web app, the flow you should use is the Authorization Code flow. Here's what happens, in order:

  1. Your app redirects Alice to Okta. The redirect URL carries your app's client_id, a redirect_uri to come back to, response_type=code, a scope that includes openid (usually profile and email too), and random state and nonce values. state lets you tell a real callback from a forged one, and nonce ties the ID token you get back to this specific request.
  2. Alice authenticates with Okta. Password, MFA, whatever Acme's admin has required. Your app never sees any of it. If Alice already has an Okta session from earlier in the day, this step takes no time at all.
  3. Okta redirects Alice back to your app with a short-lived authorization code. It arrives as a query parameter on your redirect_uri, along with the state you sent. Your app checks that state matches.
  4. Your app exchanges the code for tokens. This is a server-to-server POST to Okta's token endpoint, authenticated with your client_secret. Okta responds with an ID token, an access token, and optionally a refresh token.
  5. Your app validates the ID token. Check the signature against Okta's published public keys, confirm iss is the Okta tenant you expected, aud is your client_id, exp hasn't passed, and nonce matches the one you sent. If all of that holds, the sub claim is Alice's stable identifier and you can start a session.
OIDC Authorization Code flow sequence diagram: your app redirects Alice's browser to Okta, Alice signs in, Okta redirects back with a code, and your app exchanges the code for an ID token and access token over a server-to-server back channel.

Step 4 is the one that makes this secure, and it's the part the DMV model skipped. The browser only ever carries a one-time code. The tokens travel over a back channel between your server and the IdP, where nothing in Alice's browser history or a referrer header can leak them.

What about a single-page app or a mobile app that can't keep a client_secret? Use the same Authorization Code flow with PKCE, which replaces the secret with a one-time proof. The spec also defines an older Implicit flow that returns tokens straight to the browser. It was deprecated for exactly the leakage reasons above, so don't use it.

One more piece worth knowing: every OIDC provider publishes a discovery document at /.well-known/openid-configuration. It lists the authorization endpoint, the token endpoint, the URL for the signing keys (the JWKS), and the scopes and algorithms the provider supports. Most OIDC client libraries read it automatically, which is a big part of why OIDC integrations tend to go faster than SAML ones.

What's Inside an ID Token?

Back to the sealed note. In OIDC it's a JWT: three base64url-encoded segments separated by dots, a header, a payload, and a signature. (If JWTs are new to you, we take one apart with runnable code in JWT Authentication Explained with Code Examples.) Decode the payload of the token Alice brought back and you'd see something like this:

{
  "iss": "https://acme.okta.com",
  "sub": "00u1a2b3c4d5e6f7g8h9",
  "aud": "0oa9z8y7x6w5v4u3t2s1",
  "exp": 1790701200,
  "iat": 1790697600,
  "nonce": "n-0S6_WzA2Mj",
  "email": "alice@acme.com",
  "email_verified": true,
  "name": "Alice Chen"
}

The claims that matter most, and what to do with each:

ClaimWhat it isWhat you do with it
issIssuer: the IdP that minted the tokenReject the token if it isn't the IdP you expect
subSubject: a stable, unique ID for this user at this IdPUse this as the user's identity. Not email; emails change and aren't guaranteed unique across IdPs
audAudience: your app's client_idReject the token if it was issued for a different app
exp / iatExpiry and issued-at, as Unix timestampsReject expired tokens; most IdPs issue ID tokens that expire in an hour or less
nonceA random value your app sent in the initial requestCheck it matches, to prevent replay
email, name, pictureStandard profile claims, present if you asked for the email and profile scopesPopulate the user's record
Anatomy of an OIDC ID token: the header names the signing key, the payload carries iss, aud, exp, nonce, sub, and email claims with the check each one needs, and the signature proves the IdP issued it. Decoding is not verifying.

Two properties of the ID token trip people up.

First, the ID token is signed, not encrypted. (The spec allows encrypting it too, but almost nobody does.) The signature proves the DMV wrote the note. It doesn't stop anyone from reading it. Decoding a JWT and verifying a JWT are different operations, and only the second one tells you anything.

Second, the ID token is not an API credential. It proves who logged in. Your app consumes it once, starts a session, and moves on. Calling someone else's API with it is using the DMV's note as a credit card.

OIDC vs. SAML: What's the Difference?

From Alice's seat, OIDC and SAML look identical: click a button, authenticate at the IdP, land back in the app. The differences are in how they're built, who already uses them, and how much work they are for your team. (We walk through the SAML side in the same depth in Understanding SAML and SCIM.)

SAML 2.0OIDC
Released20052014
Built onXML and SOAP-era web standardsOAuth 2.0, JSON, REST
Token formatXML assertion, signed with XML-DSigJWT (ID token)
TransportBrowser POST of a large base64 XML blobRedirect with a short code, then a back-channel HTTPS call
Client discoveryManual metadata exchange (upload an XML file)Automatic via /.well-known/openid-configuration
Mobile and SPA supportAwkward; built for browser form postsNative; Authorization Code + PKCE is designed for it
API accessNot in scopeBuilt in; you get an access token with the ID token
Where you'll see itEnterprise IdPs: Okta, Entra ID, Ping, ADFSConsumer login (Google, Apple, Microsoft) and increasingly enterprise IdPs
Implementation difficultyHigher: XML parsing, canonicalization, and signature verification have a long history of bugsLower: JWT libraries are mature and small

Use SAML when the customer tells you to. Large enterprises with established IdP setups often have SAML configured for everything and will expect you to support it. Security questionnaires still ask "Do you support SAML?" far more often than they ask about OIDC.

Use OIDC for everything else. Social login is OIDC. Mobile and single-page apps are far easier with OIDC. Most major IdPs offer it alongside SAML, and newer integrations increasingly default to it. If you're starting from scratch and get to choose, OIDC is simpler and harder to get wrong.

In practice, a B2B product ends up supporting both. Your self-serve customers want "Sign in with Google." Your enterprise customers send you a SAML metadata file. The good news is they coexist cleanly: both produce a verified identity, and once the login is done, the rest of your app doesn't need to know which protocol was used. Neither one handles creating or removing accounts, by the way. That's SCIM, which runs alongside either.

Do You Need OIDC Yet?

You may already have it. If your app offers "Sign in with Google" or "Sign in with Microsoft," that's OIDC, and a library is handling the flow above for you. (GitHub is the notable holdout for user login: it's plain OAuth 2.0 with a user-info API bolted on, which is exactly the pattern OIDC was created to standardize.)

The version that takes real work is per-customer OIDC: letting Acme connect its own Okta tenant, and Globex connect its own Entra ID tenant, so each company's employees log in through their own IdP. That's enterprise SSO, and you'll know you need it when:

  • A prospect's security questionnaire asks "Do you support SSO with our identity provider?"
  • A customer's IT admin asks for an OIDC or SAML setup guide
  • A deal stalls on "we can't roll this out to 200 people without SSO"

If none of that has happened, you likely don't need it yet. Keep social login and come back when a customer asks.

Outside of login, you'll also meet OIDC in a few places worth recognizing: it's how kubectl authenticates against many Kubernetes clusters, how a CI job proves its identity to AWS or GCP without long-lived keys (workload identity), and it shows up in the MCP authorization spec, which is built on OAuth 2.1 and reuses OpenID Connect Discovery to locate the authorization server's endpoints.

Common OIDC Mistakes

OIDC is simpler than SAML, which is not the same as easy. These are the mistakes that come up repeatedly in code review and in breach write-ups.

Skipping ID token validation. It came back from the token endpoint over HTTPS, so it's tempting to trust it. Validate it anyway: signature, iss, aud, exp, and nonce. The whole model rests on your app checking the DMV's signature, not on the note having arrived in a sealed envelope.

Using email as the user identifier. Use sub. Emails change, get reassigned, and two different IdPs can both vouch for the same address. sub is unique per user per IdP, and the pair (iss, sub) is unique everywhere.

Trusting email without checking email_verified. Some IdPs will return an email claim for an address the user never confirmed. If you auto-link accounts by email, that's an account takeover waiting to happen.

Treating the ID token as an API credential. The ID token tells your app who logged in. Sending it to a third-party API is wrong; that's what the access token is for.

Using the Implicit flow. It leaks tokens into browser history and referrer headers. Use Authorization Code with PKCE, for SPAs and mobile too.

Hardcoding endpoints instead of using discovery. Providers rotate keys and occasionally move endpoints. Read /.well-known/openid-configuration and the JWKS URL it points to, and cache with a sensible TTL.

Ignoring state. It's the CSRF protection for the login flow. Generate it per request, store it in the session, and check it on the callback.

Where PropelAuth Fits

Everything above is the protocol. What turns it into a feature your enterprise customers can use is the layer around it, and that's where most of the engineering time goes:

  • Per-customer IdP configuration, so Acme can connect its Okta and Globex its Entra ID
  • Supporting SAML next to OIDC, because your customers won't all pick the same one
  • Mapping the identity you get back to the right organization in your app, and making sure someone from acme.com can't land in Globex's workspace
  • Carrying roles and attributes from the IdP into your app's permissions
  • Session management, refresh, logout, and the edge cases the spec leaves to you

PropelAuth handles OIDC authentication and SAML as part of Enterprise SSO, built around organizations from the start. Acme's IT admin configures their own IdP through a guided setup flow in the hosted organization settings (or through a setup link you send them), and a user who logged in via OIDC, SAML, social login, or password shows up the same way in your code: as a member of an organization with roles. Your application still owns what those roles are allowed to do; PropelAuth tells you who's asking and which organization they belong to. Add SCIM and account provisioning is handled too.

Frequently Asked Questions

Is OIDC the same as OAuth? No. OAuth 2.0 is an authorization framework; OIDC is an authentication layer built on top of it. OIDC uses OAuth's flows and adds the openid scope and the ID token. Every OIDC login is an OAuth flow, but most OAuth flows are not OIDC.

Is OIDC the same as SSO? SSO is the outcome: log in once, use many apps. OIDC and SAML are two protocols that deliver it. When an enterprise customer says "SSO," they usually mean SAML, but OIDC-based SSO is increasingly common.

Is OIDC replacing SAML? Slowly. New integrations lean toward OIDC because it's simpler and works better with mobile and single-page apps, but SAML has twenty years of enterprise deployment behind it and isn't going anywhere soon. Most B2B products end up supporting both.

Is an ID token a JWT? Yes, always. The OIDC spec requires it. Access tokens can be JWTs or opaque strings, depending on the provider.

Do I need OIDC for API authentication? Not directly. OIDC is for logging users in. Once a user is logged in, your app issues its own session or API tokens. For machine-to-machine access, you'd typically use OAuth 2.0 client credentials or API keys rather than OIDC.

Does PropelAuth support OIDC? Yes. PropelAuth added OIDC connections for enterprise SSO alongside SAML, and supports OIDC-based social login with Google, Microsoft, and others. Enterprise connections are configured per organization, and your customers can set up their own IdP without you writing integration code.