---
title: "OpenID vs OAuth 2.0 vs OIDC: Which Auth Protocol Should Your API Use?"
description: "Understand the differences between OpenID, OAuth 2.0, and OpenID Connect (OIDC), and learn which protocol fits your API's authentication and authorization needs."
canonicalUrl: "https://zuplo.com/learning-center/openid-vs-oauth-vs-oidc"
pageType: "learning-center"
authors: "nate"
tags: "API Authentication"
image: "https://zuplo.com/og?text=OpenID%20vs%20OAuth%202.0%20vs%20OIDC"
---
If you have ever tried to explain the difference between OpenID, OAuth, and OIDC
to a coworker, you know how quickly the conversation spirals. The names are
confusingly similar, the specs overlap, and most blog posts muddy the water by
treating them as interchangeable. They are not.

Each protocol solves a distinct problem. OAuth 2.0 handles **authorization** —
what an application is allowed to do. OpenID Connect (OIDC) adds an
**authentication** layer on top of OAuth 2.0 — proving _who_ the user is. And
the original OpenID (versions 1.0 and 2.0) is a now-deprecated predecessor that
OIDC replaced entirely.

Getting this wrong means choosing the wrong token type, trusting the wrong
claims, or building auth flows that break the moment you add a second identity
provider. This guide walks through each protocol, explains when to use what, and
shows how they fit into a modern API gateway.

- [Quick reference: OpenID vs OAuth 2.0 vs OIDC](#quick-reference-openid-vs-oauth-20-vs-oidc)
- [OAuth 2.0: delegated authorization](#oauth-20-delegated-authorization)
- [OpenID Connect (OIDC): authentication on top of OAuth 2.0](#openid-connect-oidc-authentication-on-top-of-oauth-20)
- [Why the original OpenID is gone](#why-the-original-openid-is-gone)
- [When to use which protocol](#when-to-use-which-protocol)
- [Common confusion: ID tokens are not access tokens](#common-confusion-id-tokens-are-not-access-tokens)
- [OIDC at the API gateway](#oidc-at-the-api-gateway)
- [How Zuplo integrates with OIDC providers](#how-zuplo-integrates-with-oidc-providers)
- [AI agent authentication and token exchange](#ai-agent-authentication-and-token-exchange)
- [Wrapping up](#wrapping-up)

## Quick reference: OpenID vs OAuth 2.0 vs OIDC

Before diving into the details, here is a high-level comparison of the three
protocols you will encounter in discussions about API auth.

**OpenID 1.0 / 2.0 (deprecated)**

- **Purpose:** Decentralized single sign-on (SSO) for the web
- **Status:** Deprecated by the OpenID Foundation. No major provider supports it
  today.
- **Replaced by:** OpenID Connect

**OAuth 2.0 ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749))**

- **Purpose:** Delegated authorization — letting an app access resources on
  behalf of a user without sharing credentials
- **Core tokens:** Access token, refresh token
- **Does not define:** A standard way to get user identity

**OpenID Connect (OIDC)
([Core 1.0 spec](https://openid.net/specs/openid-connect-core-1_0.html))**

- **Purpose:** Authentication — verifying who the user is — built as a layer on
  top of OAuth 2.0
- **Core tokens:** ID token (a
  [JWT](https://datatracker.ietf.org/doc/html/rfc7519)), plus the OAuth 2.0
  access token and refresh token
- **Key additions:** `/.well-known/openid-configuration` discovery, standardized
  claims (`sub`, `email`, `name`), UserInfo endpoint

The short version: OAuth 2.0 answers _"What can this app do?"_ OIDC answers
_"Who is this user?"_ Old OpenID answered the same identity question, but OIDC
does it better by building on OAuth 2.0 instead of reinventing the wheel.

## OAuth 2.0: delegated authorization

OAuth 2.0 is an authorization framework defined in
[RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749). It lets a user grant
a third-party application limited access to their resources without handing over
their password. The classic analogy is the valet key — it starts the car but
cannot open the trunk.

### The four roles

OAuth 2.0 defines four participants:

- **Resource owner** — the user who owns the data
- **Client** — the application requesting access
- **Authorization server** — issues tokens after verifying consent
- **Resource server** — the API that holds the protected data

### Grant types

Different situations call for different flows. OAuth 2.0 defines several grant
types:

- **Authorization Code** — the standard for web and mobile apps. The client
  redirects the user to the authorization server, receives a short-lived code,
  and exchanges it for tokens server-side.
- **Authorization Code with PKCE** — adds a code verifier to protect public
  clients (SPAs, native apps) from authorization code interception.
  [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) defines this
  extension, and it is now recommended for all clients — not just public ones.
- **Client Credentials** — for server-to-server communication where no user is
  involved. The client authenticates with its own credentials and receives an
  access token directly. This is the go-to pattern for machine-to-machine (M2M)
  integrations, data pipelines, and backend services.
- **Device Code** — for devices with limited input capabilities (smart TVs, CLI
  tools) that cannot easily handle browser redirects.

### Access tokens and scopes

OAuth 2.0 access tokens are the keys that unlock API resources. They carry
[scopes](/learning-center/custom-scopes-in-oauth-guide) — strings like
`read:users` or `write:projects` — that define exactly what the token holder is
allowed to do.

Access tokens are deliberately short-lived (minutes to hours). If one leaks, the
blast radius is limited. Refresh tokens let the client obtain new access tokens
without re-prompting the user.

One crucial detail: the OAuth 2.0 spec does not define a standard format for
access tokens. They can be opaque strings, JWTs, or anything else. The spec also
does not define a standard way to convey user identity. That gap is exactly what
OIDC fills.

For a deeper dive into OAuth 2.0 implementation, see our
[developer's guide to securing your API with OAuth 2.0](/learning-center/securing-your-api-with-oauth).

## OpenID Connect (OIDC): authentication on top of OAuth 2.0

OpenID Connect is an identity layer built directly on OAuth 2.0. Published in
2014 by the OpenID Foundation, OIDC adds a standardized way for clients to
verify the identity of end-users and obtain basic profile information.

The relationship is straightforward: every OIDC flow is an OAuth 2.0 flow with
extra parameters. When a client includes `openid` in the `scope` parameter of an
authorization request, the authorization server returns an **ID token**
alongside the access token.

### The ID token

The ID token is a [JWT](/learning-center/jwt-api-authentication) that contains
claims about the authenticated user. Standard claims defined by the OIDC Core
spec include:

- `sub` — a unique identifier for the user
- `iss` — the issuer (your identity provider)
- `aud` — the intended audience (your application's client ID)
- `exp` — expiration timestamp
- `iat` — issued-at timestamp
- `nonce` — ties the token to a specific authentication request (prevents replay
  attacks)
- `email` — the user's email address
- `name` — the user's display name

Because the ID token is a signed JWT, the client can verify it locally by
checking the signature against the provider's public keys — no network call
required.

### Discovery and JWKS

OIDC standardizes how clients find provider configuration through a **well-known
discovery endpoint**:

```
https://your-idp.com/.well-known/openid-configuration
```

This document tells clients everything they need: the authorization endpoint,
token endpoint, supported scopes, signing algorithms, and crucially, the **JWKS
URI** — the URL where the provider publishes the public keys used to sign
tokens.

The JSON Web Key Set (JWKS) endpoint, defined in
[RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517), returns the current
set of public keys. Clients and API gateways cache these keys and use them to
verify JWT signatures without calling the identity provider on every request.

### The UserInfo endpoint

OIDC also defines a `/userinfo` endpoint that returns additional claims about
the authenticated user. While the ID token contains a minimal set of claims, the
UserInfo endpoint can return richer profile data — useful when the client needs
information beyond what fits in a compact token.

## Why the original OpenID is gone

OpenID 1.0 (2005) and OpenID 2.0 (2007) were early attempts at decentralized web
authentication. The idea was simple: use a URL as your identity and let any site
verify it against an OpenID provider.

The protocol had real traction for a while — major sites including Google,
Yahoo, and Stack Overflow supported it. But it had limitations that became
deal-breakers as the web evolved:

- **No built-in authorization** — OpenID only handled "who are you?" without a
  mechanism for "what are you allowed to do?"
- **Poor mobile support** — the redirect-based flow assumed desktop browsers
- **Limited profile data** — getting user attributes required nonstandard
  extensions
- **Fragmented ecosystem** — different providers implemented extensions
  inconsistently

Google deprecated OpenID 2.0 support in 2015. The OpenID Foundation officially
classified OpenID 2.0 as obsolete, publishing a
[migration guide](https://openid.net/specs/openid-connect-migration-1_0.html)
for moving to OIDC. By 2019, virtually all major providers had dropped support.

OIDC solved every shortcoming by building on OAuth 2.0's battle-tested
authorization framework and adding identity on top. It is the clear successor,
and "OpenID" in modern usage almost always means OpenID Connect.

## When to use which protocol

Choosing the right protocol depends on what your API actually needs to know.

### OAuth 2.0 alone: third-party apps accessing your API

Use OAuth 2.0 when a third-party application needs to access your API on behalf
of a user. The application does not need to know _who_ the user is — it just
needs a token that proves the user said "yes, this app can read my data."

Typical scenarios:

- A Slack bot reading a user's calendar events
- A CI/CD pipeline deploying to your infrastructure
- A third-party analytics dashboard pulling usage metrics

### OIDC: federated login and identity claims

Use OIDC when your application or API needs to verify user identity — not just
authorization. OIDC gives you a standardized ID token with claims you can trust,
issued by a provider like Auth0, Okta, Azure AD (Entra ID), or Google.

Typical scenarios:

- Single sign-on (SSO) across multiple applications
- A gateway that routes requests or applies policies based on user identity
- Any API that needs to know _who_ is calling, not just _what_ they are allowed
  to do

### Client Credentials: server-to-server and agent-to-API

Use the OAuth 2.0 Client Credentials grant when there is no user in the picture
— just two machines talking to each other. The client authenticates with its own
credentials (client ID and secret) and receives an access token directly.

Typical scenarios:

- A backend microservice calling another internal API
- A data pipeline pulling records from your API on a nightly schedule
- An AI agent making autonomous API calls with its own identity

### Summary of when to use each

**OAuth 2.0 (Authorization Code / PKCE)**

- Goal: Authorize a third-party app to access user resources
- User involved: Yes
- Identity returned: No (unless you also request OIDC scopes)

**OIDC**

- Goal: Authenticate a user and get verified identity claims
- User involved: Yes
- Identity returned: Yes (ID token with `sub`, `email`, `name`, etc.)

**OAuth 2.0 (Client Credentials)**

- Goal: Authenticate a machine or service
- User involved: No
- Identity returned: The client itself is the identity

## Common confusion: ID tokens are not access tokens

This is the single most common mistake developers make with OIDC, and it creates
real security vulnerabilities.

**ID tokens** prove who the user is. They are intended for the **client
application** — the frontend, the mobile app, the backend that initiated the
login. The client reads the claims to personalize the UI, create a session, or
make authorization decisions.

**Access tokens** prove what the holder is allowed to do. They are intended for
the **resource server** (your API). The API validates the access token and uses
its scopes to determine what data to return.

Here is why the distinction matters:

- **Never send an ID token to an API as a bearer token.** The ID token's `aud`
  claim is your application's client ID, not your API's identifier. An API that
  accepts ID tokens as access tokens is trusting a token that was not issued for
  it.
- **Never use an access token to determine who the user is in your client app.**
  The access token's format is not guaranteed by the OAuth 2.0 spec. Even if it
  happens to be a JWT today, the authorization server can change the format
  without notice.

The correct pattern: use the ID token in your client to establish a session, and
send the access token to your API in the `Authorization` header.

## OIDC at the API gateway

An API gateway is the natural place to validate OIDC tokens. Instead of every
microservice implementing its own JWT verification logic, the gateway handles it
once, at the edge, before traffic reaches your backend.

### What the gateway validates

A properly configured gateway checks every incoming request:

1. **Signature verification** — the JWT's signature is verified against the
   identity provider's public keys (fetched from the JWKS endpoint)
2. **Issuer validation** — the `iss` claim must match the expected identity
   provider
3. **Audience validation** — the `aud` claim must match your API's identifier
4. **Expiration check** — the `exp` timestamp must be in the future
5. **Claim extraction** — verified claims are extracted and passed to downstream
   services for authorization decisions

### JWKS rotation

Identity providers rotate their signing keys periodically. A robust gateway
caches the JWKS and refreshes it automatically when it encounters a token signed
by an unknown key. This avoids both hitting the provider on every request and
breaking when keys rotate.

### From authentication to authorization

Once the gateway has verified _who_ the user is (authentication), downstream
services can use the extracted claims for _what they can do_ (authorization).
Common patterns include:

- **Scope-based access control** — checking `scope` claims against required
  permissions for each endpoint
- **Role-based access control (RBAC)** — mapping a `roles` claim to allowed
  operations
- **Claim-driven routing** — forwarding requests to different backends based on
  `org_id` or `tenant` claims

## How Zuplo integrates with OIDC providers

Zuplo's approach to OIDC is programmable and provider-agnostic. Token validation
runs at the edge across 300+ data centers, so authentication decisions happen
close to the caller without a round-trip to a central auth service.

### The OpenID JWT Auth Policy

The core building block is the
[OpenID JWT Auth Policy](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound).
It works with any OIDC-compliant identity provider — you point it at a JWKS URL,
set the expected issuer and audience, and the gateway handles signature
verification, expiration checks, and claim extraction automatically.

```json
{
  "name": "oidc-auth",
  "policyType": "open-id-jwt-auth-inbound",
  "handler": {
    "export": "OpenIdJwtInboundPolicy",
    "module": "$import(@zuplo/runtime)",
    "options": {
      "issuer": "$env(AUTH_ISSUER)",
      "audience": "$env(AUTH_AUDIENCE)",
      "jwkUrl": "https://your-idp.com/.well-known/jwks.json"
    }
  }
}
```

After successful validation, the token's claims are available on `request.user`:

- `request.user.sub` — the subject identifier from the JWT
- `request.user.data` — all other claims from the token

### Provider-specific policies for faster setup

While the generic OpenID JWT policy works with any provider, Zuplo also offers
pre-configured policies for common identity platforms:

- [Auth0](https://zuplo.com/docs/policies/auth0-jwt-auth-inbound)
- Okta
- Clerk
- AWS Cognito
- Firebase
- Supabase

These are convenience wrappers around the same JWT validation logic, with
provider-specific defaults already set (like JWKS URLs and issuer formats).

### Claim-driven authorization in TypeScript

Because Zuplo is a programmable gateway, you can go beyond declarative policy
configuration and write TypeScript to make authorization decisions based on any
claim. For example, checking an `org_id` claim:

```typescript
import { ZuploContext, ZuploRequest, HttpProblems } from "@zuplo/runtime";

export default async function (request: ZuploRequest, context: ZuploContext) {
  const orgId = request.user?.data?.org_id;

  if (orgId !== "expected-org-id") {
    return HttpProblems.forbidden(request, context);
  }

  return request;
}
```

Zuplo also includes a built-in
[JWT Scope Validation Policy](https://zuplo.com/docs/policies/jwt-scopes-inbound)
for checking scopes without writing custom code.

### API key + JWT on the same route

Real-world APIs often need to support both API keys (for machine clients) and
JWTs (for user-facing apps) on the same endpoint. Zuplo handles this with the
[Composite Inbound Policy](https://zuplo.com/docs/policies/composite-inbound),
which chains multiple auth policies together. The first policy that succeeds
populates `request.user`, and downstream handlers do not need to know which
credential type was used.

For a full walkthrough of this pattern, see
[Using JWT and API Key Auth on the Same Route](/blog/using-jwt-and-api-key-auth-on-the-same-route).

## AI agent authentication and token exchange

As AI agents become first-class API consumers, the authentication model is
evolving. Two patterns are emerging.

### Client Credentials for autonomous agents

When an AI agent acts on its own behalf — not on behalf of a specific user — the
OAuth 2.0 Client Credentials grant is the right fit. The agent authenticates
with its own `client_id` and `client_secret`, receives a scoped access token,
and calls your API. The API sees the agent as a known, authorized machine
identity, subject to its own rate limits and permissions.

This is exactly how you would authenticate a backend microservice today. Nothing
special is needed on the protocol side.

### Token exchange for agents acting on behalf of users

The more interesting case is when an agent needs to call an API **on behalf of a
specific user**. The user has authenticated with the agent, and now the agent
needs to prove both its own identity _and_ the user's identity to a downstream
API.

[RFC 8693 (OAuth 2.0 Token Exchange)](https://datatracker.ietf.org/doc/html/rfc8693)
defines a standard mechanism for this. The agent presents its existing token
(the `subject_token`) to the authorization server and receives a new token
scoped to the downstream API, with the user's identity preserved.

This pattern is critical for:

- **Preserving user identity across service boundaries** — the downstream API
  can enforce per-user access policies, not just per-agent ones
- **Audit trails** — logs show which user authorized which action, even through
  multiple layers of delegation
- **Least-privilege access** — the exchanged token is scoped to exactly what the
  downstream API needs

Token exchange is gaining traction in the AI ecosystem. Auth0's
[Token Vault](https://auth0.com/blog/auth0-token-vault-secure-token-exchange-for-ai-agents/)
implements RFC 8693 specifically for connecting AI agents to third-party
services securely. As the Model Context Protocol (MCP) ecosystem matures, expect
token exchange to become the standard way agents propagate identity.

## Wrapping up

The protocols are distinct, and using the right one matters:

- **OAuth 2.0** handles authorization — granting an app access to resources
- **OIDC** handles authentication — verifying who the user is, using OAuth 2.0
  as the underlying transport
- **The original OpenID** is deprecated and replaced entirely by OIDC
- **ID tokens** are for clients; **access tokens** are for APIs — do not mix
  them up
- **Client Credentials** is the right OAuth 2.0 grant for machine-to-machine and
  agent-to-API communication
- **Token exchange (RFC 8693)** is the emerging pattern for agents acting on
  behalf of users

At the API gateway level, OIDC validation should happen once, at the edge,
before traffic hits your backend. Zuplo's
[OpenID JWT Auth Policy](https://zuplo.com/docs/policies/open-id-jwt-auth-inbound)
makes this straightforward — point it at any OIDC provider's JWKS endpoint, and
the gateway handles signature verification, issuer checks, audience validation,
and claim extraction automatically.

If you are building or securing APIs and want to see how this works in practice,
[sign up for Zuplo](https://portal.zuplo.com/signup) and try adding JWT
authentication to a route in under five minutes.