OAuth 2.0 and OpenID Connect Explained
Learn how OAuth 2.0 handles authorization and OpenID Connect adds user identity — the standards behind Sign in with Google, SSO, and API access control.
Introduction
Every time you click “Sign in with Google” or “Continue with GitHub,” two protocols are working together to make that happen securely: OAuth 2.0 handles authorization — deciding what the application is allowed to do on your behalf — while OpenID Connect handles identity — proving who you actually are. Together they power the authentication and authorization infrastructure of the modern web. In 2023, Google reported that OAuth-based Sign-In with Google processes over a billion authentication events per day. Yet despite their ubiquity, OAuth 2.0 and OIDC are frequently misunderstood and misimplemented — leading to token leakage, open redirect vulnerabilities, and broken access control. The OWASP API Security Top 10 lists Broken Object Level Authorization (BOLA) and Broken Authentication among the most critical API vulnerabilities, and both often trace back to OAuth implementation errors. Understanding how OAuth 2.0 and OpenID Connect actually work is essential for any developer building or securing modern applications.
What Is OAuth 2.0?
OAuth 2.0 (defined in RFC 6749) is an authorization delegation framework. It answers one question: can this application access this resource on behalf of this user? OAuth 2.0 is explicitly not an authentication protocol — it says nothing about who the user is, only what the application is permitted to do.
The framework defines four roles. The Resource Owner is the user who owns the data. The Client is the application requesting access. The Authorization Server is the identity provider (Google, GitHub, Azure AD, Okta) that authenticates the user and issues tokens. The Resource Server is the API that the client wants to call — it accepts and validates access tokens.
The key innovation of OAuth 2.0 over earlier approaches is scope-limited access tokens: instead of sharing a password, the user explicitly approves a list of scopes (e.g., “read your email,” “post to your calendar”) and the authorization server issues a token that is only valid for those scopes on that resource server, for a limited time.
What Is OpenID Connect?
OpenID Connect (OIDC) is a thin identity layer built on top of OAuth 2.0. It adds the ability for the authorization server to communicate who the authenticated user is — not just what permissions were granted. OIDC introduces two additions:
- An ID Token: a signed JSON Web Token (JWT) containing identity claims about the user — their unique subject identifier (
sub), name, email, and the time of authentication (auth_time). - A UserInfo endpoint: a protected API endpoint at the authorization server that the client can call with an access token to retrieve additional user profile claims.
Where OAuth 2.0 alone gives you “this token can read the user’s calendar,” OIDC adds “and this is the user: their ID is 12345, their name is Jane Doe, their email is jane@example.com.” This is what makes “Sign in with Google” work — Google acts as the OIDC provider, issuing an ID Token that the relying party (your application) can verify to establish the user’s identity.
How OAuth 2.0 and OpenID Connect Work
The Authorization Code flow with PKCE is the current best-practice flow for all applications:
- PKCE setup: The client generates a cryptographically random code verifier (43–128 characters), computes its SHA-256 hash as the code challenge, and stores the verifier locally.
- Authorization request: The client redirects the user’s browser to the authorization server’s
/authorizeendpoint with parameters includingresponse_type=code, the requestedscope(e.g.,openid email profile), aredirect_uri, astatevalue (CSRF protection), and thecode_challenge. - User authentication and consent: The authorization server authenticates the user (password, MFA, SSO) and displays a consent screen listing the requested scopes. The user approves or denies.
- Authorization code issued: The server redirects back to the client’s
redirect_uriwith a short-lived, one-time authorization code and the originalstatevalue. The code is only valid for seconds to minutes. - Token exchange: The client’s backend makes a direct server-to-server HTTPS POST to the authorization server’s
/tokenendpoint, exchanging the authorization code plus the original code verifier for tokens. Including the code verifier proves this is the same client that started the flow. - Token response: The authorization server returns an access token (for calling the resource server), an ID Token (if
openidscope was requested — for user identity), and optionally a refresh token (for obtaining new access tokens without re-authenticating). - Resource access: The client includes the access token in the
Authorization: Bearer <token>header when calling the resource server API.
Authorization Code flow with PKCE: the authorization code travels through the browser but is worthless without the code verifier. The actual token exchange happens server-to-server, keeping tokens out of browser history and referrer headers.
OAuth 2.0 vs OpenID Connect vs SAML
| Feature | OAuth 2.0 | OpenID Connect (OIDC) | SAML 2.0 |
|---|---|---|---|
| Purpose | Authorization (API access delegation) | Authentication + Authorization | Authentication + SSO |
| Token format | Access token (JWT or opaque), Refresh token | ID Token (JWT) + Access token | XML SAML Assertion |
| Transport | JSON over HTTPS | JSON over HTTPS | XML over HTTP POST/Redirect |
| Primary use case | API access, third-party app authorization | ”Sign in with Google/Apple/GitHub” | Enterprise SSO (ADFS, Okta SAML) |
| Mobile/SPA support | Excellent (PKCE) | Excellent (PKCE + ID Token) | Poor (no browser-native support) |
| Spec complexity | Medium | Medium (adds ~20 pages to OAuth 2.0) | High (XML schema, extensive spec) |
| Adoption trend | Universal in APIs | Growing — replacing SAML in new apps | Dominant in legacy enterprise SSO |
If your organization still runs XML-based enterprise SSO, see how SAML authentication works for a deeper look at that protocol and why newer apps are migrating away from it.
Real-World Use Cases
Third-party app authorization: When a project management tool asks “can we read your Google Calendar to schedule meetings?”, OAuth 2.0 handles this with the calendar read scope. The user grants access; the app gets an access token scoped only to calendar reads; Google’s resource server validates the token on each API call. The user’s Google password is never shared with the project management tool. For a related look at how mutual authentication works for server-to-server APIs, see mTLS Explained: Mutual TLS for Zero-Trust APIs.
Enterprise Single Sign-On: Organizations configure their identity provider (Azure AD, Okta, Auth0) as an OIDC provider. Employees authenticate once, and all connected SaaS applications accept the ID Token as proof of identity. The OIDC sub claim uniquely identifies the user across applications. When an employee leaves the organization, revoking their account at the IdP immediately blocks access to all OIDC-connected applications.
Machine-to-machine API authorization: The OAuth 2.0 Client Credentials flow (no user involved) lets backend services authenticate to APIs using a client ID and secret, receiving a scoped access token. This is the standard pattern for microservice-to-microservice authorization — each service holds its own client credentials and requests tokens scoped to the specific APIs it needs to call. Access is auditable and revocable per-client without touching other services.
Common Mistakes to Avoid
Using the Implicit flow: The Implicit flow — where tokens are returned directly in the URL fragment after the authorization redirect — is officially deprecated by RFC 9700. URL fragments appear in browser history, are sent in Referer headers to third-party scripts on the page, and can be intercepted in shared environments. All new applications must use Authorization Code with PKCE, including single-page apps and mobile apps. Never implement the Implicit flow in new code.
Skipping state parameter validation: The state parameter in the authorization request must be a cryptographically random, unguessable value that the client verifies on the redirect callback. Failing to validate state enables Cross-Site Request Forgery (CSRF) attacks where an attacker tricks a user into completing an OAuth flow that authorizes the attacker’s account. This is one of the most common OAuth implementation errors.
Storing tokens in localStorage: Access tokens and ID Tokens stored in localStorage are accessible to any JavaScript running on the page — including malicious scripts injected via XSS. Store tokens in memory (for SPAs) or in httpOnly, Secure cookies (for server-rendered apps). An attacker who successfully injects XSS into a page with tokens in localStorage gains full API access without the user’s knowledge.
Not validating ID Token signatures: An ID Token is only trustworthy after verifying its JWT signature against the authorization server’s public keys (available at the /.well-known/jwks.json endpoint). Accepting an ID Token without signature verification means any attacker who can forge a JWT can impersonate any user. Always use a well-tested JWT library that validates signature, expiry (exp), audience (aud), and issuer (iss) claims.
Getting Started
To implement OAuth 2.0 and OpenID Connect correctly:
First, use an existing library — never roll your own OAuth client. For web applications, use a battle-tested OIDC client library: openid-client for Node.js, authlib for Python, spring-security-oauth2 for Java, or platform SDKs from your IdP (Auth0, Okta, Microsoft MSAL). These handle PKCE, state management, token validation, and refresh token rotation correctly.
Second, register your redirect URIs exactly with your authorization server. OAuth 2.0’s security depends on the authorization server only redirecting to pre-registered URIs. Overly permissive redirect URI patterns (e.g., wildcard subdomains) can allow attackers to redirect authorization codes to attacker-controlled endpoints. Register exact URIs only.
Third, implement token refresh with rotation. Refresh tokens are long-lived credentials and must be protected accordingly. Implement refresh token rotation — each use of a refresh token issues a new refresh token and invalidates the old one. Store refresh tokens in an httpOnly cookie or a server-side session, never in browser-accessible storage. For access token encryption in transit, your TLS configuration (see TLS Handshake Explained) provides the transport-layer protection.
Fourth, follow RFC 9700’s security BCP. RFC 9700 (“OAuth 2.0 Security Best Current Practice,” 2025) is the authoritative checklist for modern OAuth deployments. Key requirements include: always using PKCE, using response_mode=form_post instead of fragment, binding tokens to the client’s DPoP key for public clients, and rotating refresh tokens. For the underlying public-key cryptography that JWT signatures depend on, see Public Key vs Private Key: How They Work Together.
FAQ
Common questions — answered in plain English.
What is OAuth 2.0?
What is the difference between OAuth 2.0 and OpenID Connect?
What is an access token?
What is the difference between the Authorization Code flow and the Implicit flow?
What is PKCE and why does it matter?
What is an ID Token in OpenID Connect?
References
- [1]
- [2]OpenID Connect Core 1.0OpenID Foundation, 2014
- [3]
- [4]
- [5]RFC 7519: JSON Web Token (JWT)IETF, 2015