← Back to Security

OAuth 2.0: What It Is and How It Works

OAuth 2.0 is an open standard authorization protocol that enables secure, delegated access to resources without sharing passwords. Instead of giving an application your credentials, OAuth 2.0 lets you grant limited permissions directly to that app—keeping your actual password safe while maintaining control over what data it can access.

Why OAuth 2.0 Exists

Before OAuth 2.0 became widespread, users faced a security nightmare. Want to let a third-party app access your Google contacts? You'd give it your Google password. That app could then access everything—emails, files, photos—without any restrictions. If the app got hacked, your credentials were compromised.

OAuth 2.0 solves this by introducing a middleman—the authorization server. Instead of sharing your password with every service, you authenticate once with a trusted provider (like Google, GitHub, or Facebook), then grant specific permissions to the requesting application. The app receives an access token—a temporary credential that grants only the permissions you approved.

This approach protects you and benefits service providers too. They don't need to store passwords, reduce breach risk, and maintain an audit trail of what data gets accessed and when.

Core Components of OAuth 2.0

Understanding OAuth 2.0 requires knowing four key players in the flow:

1. Resource Owner

That's you. The person who owns the data and controls permissions. You decide what the application can do with your information.

2. Client Application

The app requesting access to your data. It could be a web app, mobile app, or desktop application. The client never sees your password—it only handles tokens.

3. Authorization Server

The trusted intermediary that verifies your identity and issues tokens. Google's OAuth server, GitHub's OAuth server, and Facebook's are examples. It handles the login process and decides whether to grant the client application access.

4. Resource Server

The API that actually holds your data. When the client has a valid token, it contacts the resource server to fetch information. Often, the authorization server and resource server are the same organization.

OAuth 2.0 Grant Types and Flows

OAuth 2.0 defines several "grant types"—different methods for exchanging user credentials for access tokens. Choosing the right one depends on your application architecture.

Authorization Code Flow

This is the most common flow, especially for web applications. Here's how it works:

  1. User clicks "Login with Google" on a website
  2. The website redirects them to Google's authorization server
  3. User logs in and sees a permission screen: "This app wants access to your email"
  4. User clicks "Allow"
  5. Google redirects back to the website with an authorization code
  6. The website's backend exchanges that code for an access token (this happens server-to-server, securely)
  7. The website uses the access token to fetch user data from Google's API

The critical step is #6: the authorization code is exchanged for the token on the backend, never exposing the token to the browser. This prevents malicious scripts from stealing tokens.

Implicit Flow

Older single-page applications (SPAs) used this flow, but it's now considered less secure. The browser receives the access token directly, making it vulnerable to XSS attacks. OAuth 2.0 security best practices now recommend PKCE (Proof Key for Code Exchange) for SPAs instead.

Client Credentials Flow

Used when an application needs to access resources on its own behalf, not a user's behalf. For example, a backend service calling another API. The client directly provides its credentials to get a token.

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Resource Owner Password Credentials Flow

The user provides their username and password directly to the client application. The client then exchanges these for an access token. This flow requires high trust and is rarely recommended since it exposes credentials to the client.

Access Tokens and Refresh Tokens

Once you've granted permission, the client gets two types of tokens:

Access Token

A temporary credential that grants access to protected resources. It typically expires within hours (often 1-2 hours). When you make an API request, you include it in the Authorization header:

GET /api/user/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Refresh Token

A long-lived credential (days or months) that lets the client get a new access token without asking you to log in again. When the access token expires, the client exchanges the refresh token for a fresh access token.

POST /token HTTP/1.1
Host: authorization-server.com
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token=YOUR_REFRESH_TOKEN
&client_id=YOUR_CLIENT_ID
&client_secret=YOUR_CLIENT_SECRET

Refresh tokens stay on the backend and are never exposed to the browser, making them much safer than access tokens.

Practical OAuth 2.0 Implementation Example

Let's walk through a real scenario: a web app using "Login with GitHub."

Step 1: Register Your Application

You register your app with GitHub and receive a Client ID and Client Secret:

Client ID: a1b2c3d4e5f6g7h8
Client Secret: xyz789abc456def123

Step 2: Redirect to Authorization Endpoint

Your login button links to GitHub's authorization endpoint:

https://github.com/login/oauth/authorize?
client_id=a1b2c3d4e5f6g7h8
&redirect_uri=https://yourapp.com/callback
&scope=user:email
&state=randomstring123

The state parameter prevents CSRF attacks by verifying that the response came from the same browser that made the request.

Step 3: User Authorizes

GitHub shows the user what permissions you're requesting (user:email in this case). The user clicks "Authorize."

Step 4: Handle the Callback

GitHub redirects to your callback URL with an authorization code:

https://yourapp.com/callback?
code=abc123def456
&state=randomstring123

Step 5: Exchange Code for Token (Backend)

Your backend verifies the state parameter, then exchanges the code for a token. This happens server-to-server:

POST https://github.com/login/oauth/access_token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=a1b2c3d4e5f6g7h8
&client_secret=xyz789abc456def123
&code=abc123def456

GitHub responds with an access token:

{
  "access_token": "ghu_16C7e42F292c6912E7710c838347Ae178B4a",
  "expires_in": 28800,
  "token_type": "bearer"
}

Step 6: Fetch User Data

Now your app can request the user's GitHub profile:

GET https://api.github.com/user HTTP/1.1
Authorization: Bearer ghu_16C7e42F292c6912E7710c838347Ae178B4a

You get back user details (email, name, avatar, etc.) and can create a session for them.

OAuth 2.0 vs. OpenID Connect

A common point of confusion: OAuth 2.0 is for authorization (granting permissions), while OpenID Connect is for authentication (verifying identity).

OpenID Connect layers on top of OAuth 2.0, adding an ID token that contains information about the user. If you only need to let users log in, OpenID Connect is the better choice. If you need API access permissions, OAuth 2.0 is correct. Many modern providers support both.

Security Best Practices

OAuth 2.0 is secure by design, but implementation matters. Follow these practices:

Real-World Use Cases

You encounter OAuth 2.0 everywhere:

Common Pitfalls

Even with OAuth 2.0, mistakes happen:

OAuth 2.0 has become the industry standard for delegated access, powering billions of login interactions daily. Understanding how it works isn't just useful for engineers building authentication systems—it's essential knowledge for anyone developing secure, modern applications.

Frequently Asked Questions

Is OAuth 2.0 the same as JWT?

No. OAuth 2.0 is an authorization protocol that defines how to grant access. JWT (JSON Web Token) is a token format. OAuth 2.0 can use JWTs as access tokens, but it doesn't have to. You could use OAuth 2.0 with opaque token strings instead. Many implementations combine