Every time you click "Sign in with Google" or connect a third-party app to your GitHub account, OAuth 2.0 is running behind the scenes. It's the protocol that lets users grant apps access to their data — without handing over their password.
In this guide, you'll learn exactly how OAuth 2.0 works: the authorization flow step by step, what the different grant types are for, how it compares to OIDC and JWT, and when to use each.
What Is OAuth 2.0?
OAuth 2.0 is an open authorization framework that allows a user to grant a third-party application limited access to their account on another service — without sharing their password.
Think of it like a hotel key card system. Instead of giving a guest your master key (your password), the hotel (authorization server) issues a temporary key card (access token) that only opens specific doors (scopes) for a limited time.
OAuth 2.0 is authorization, not authentication. It answers "what can this app access?" — not "who is this user?" (That's what OpenID Connect adds on top.)
The Four Actors in OAuth 2.0
Before walking through the flow, it helps to know the four parties involved:
The OAuth 2.0 Authorization Code Flow: Step by Step
The most common and secure OAuth 2.0 flow is the Authorization Code flow. Here's exactly what happens, step by step:

Step 1: The Client Redirects the User to the Authorization Server
The flow starts when the user clicks "Login with Google" (or similar). The client redirects them to the authorization server with a URL like this:
Key parameters:
Step 2: The User Logs In and Grants Consent
The authorization server shows the user a login page and a consent screen — "This app wants access to your email and profile. Allow?" — and the user approves or denies.
Step 3: The Authorization Server Returns an Authorization Code
After the user approves, the authorization server redirects back to your redirect_uri with a short-lived authorization code:
This code is temporary (usually expires in 60–120 seconds) and can only be used once. It's not an access token — your server still needs to exchange it.
Step 4: The Client Exchanges the Code for an Access Token
Your server makes a back-channel POST request to the token endpoint — this happens server-side, so the client secret is never exposed to the browser:
The authorization server returns:
Step 5: Use the Access Token to Call the Resource Server
The resource server validates the token and returns the protected data. When the access token expires, use the refresh token to get a new one without prompting the user again.
OAuth 2.0 Grant Types
The Authorization Code flow above is just one of several OAuth 2.0 grant types. Each one is designed for a specific scenario:
For a deeper explanation of each grant type and when to use them, see our guide on OAuth 2.0 grant types.
OAuth 2.0 vs OpenID Connect (OIDC)
This is one of the most common points of confusion. Here's the short version:
OIDC is built on top of OAuth 2.0. It adds an id_token (a JWT containing user identity info) to the standard OAuth flow, plus a /userinfo endpoint and a standardized openid scope.
In practice: when you add scope=openid to your OAuth 2.0 request, you're using OIDC. When you only ask for scope=read:email (no openid), you're using plain OAuth 2.0.
Most modern implementations use both together. For a deeper comparison with SAML, see OIDC vs SAML. You can also inspect any OIDC provider's configuration using the OIDC Discovery Endpoint Explorer.
OAuth 2.0 vs JWT
OAuth 2.0 and JWT are often mentioned together but they're different things:
JWTs are commonly used as OAuth 2.0 access tokens or ID tokens, but OAuth 2.0 doesn't require JWTs. An OAuth 2.0 access token could be an opaque random string — the resource server just validates it with the authorization server.
When you receive a JWT as your OAuth 2.0 access token, you can decode and verify it locally without making a network call. When you receive an opaque token, you must call the authorization server's introspection endpoint to validate it.
Scopes: Controlling What Access Is Granted
Scopes define exactly what the client is allowed to do. They're space-separated strings in the authorization request:
The user sees these scopes on the consent screen and can approve or deny. The access token issued is then limited to those scopes — even if the resource server would otherwise allow more.
Good scope design follows the principle of least privilege: request only what you actually need. Requesting calendar.readonly instead of calendar signals to users that you won't modify their calendar.
PKCE: Required for Public Clients
If your client is a mobile app, single-page app (SPA), or any application where you can't safely store a client secret, you must use PKCE (Proof Key for Code Exchange). Public clients can't store secrets because their code ships to the user's device — anyone can inspect a mobile app bundle or browser JavaScript to extract a hardcoded secret.
PKCE prevents authorization code interception attacks by having your client generate a random code_verifier, hash it into a code_challenge, and send the challenge with the authorization request. When exchanging the code for a token, your client sends the original verifier — proving it's the same client that started the flow.
See our detailed guide on how PKCE works in OAuth 2.0.
Common OAuth 2.0 Mistakes to Avoid
Implementing OAuth 2.0 Without the Complexity
Implementing OAuth 2.0 from scratch means handling token storage, refresh logic, PKCE, state validation, scope management, and security edge cases. Most teams are better served by an authentication platform that handles this for them.
Authgear provides a fully OAuth 2.0 and OIDC compliant authorization server with pre-built login UI, token management, refresh rotation, and support for social login providers (Google, Apple, Facebook) out of the box. You get a production-ready OAuth 2.0 implementation without building the infrastructure yourself.
Summary
Here's what you need to remember about how OAuth 2.0 works:




