DEV Community

Kirill Tolmachev
Kirill Tolmachev

Posted on

OAuth Explained Like You're 5

The Problem: "Just Give Me Your Password"

Imagine you've just signed up for a cool new app that wants to organize your Google contacts into groups. The app needs access to your contacts, so it asks: "Hey, what's your Google password?"

You'd hesitate, right? And you should. Handing over your password to a third-party app is like giving a stranger the master key to your house because they offered to water your plants. Sure, they might only water the plants — but they could also rummage through your drawers, read your mail, and rearrange your furniture. You have no control over what they do once they have that key, and no easy way to take it back without changing all your locks.

This is the exact problem OAuth was designed to solve. Instead of sharing your credentials with every app that needs a sliver of access, OAuth lets you grant limited, revocable permissions without ever revealing your password. Think of it as the difference between handing someone your car keys and handing them a valet key — the valet key starts the engine and opens the driver's door, but it won't unlock the trunk or the glove box.

The Valet Key Analogy

Let's stick with this valet key idea because it maps beautifully onto how OAuth actually works.

When you pull up to a fancy restaurant and hand your car to the valet, you don't give them your entire keychain — house keys, office keys, safe deposit box key. You hand them a special key that only lets them drive the car a short distance. They can't open your trunk, they can't access your personal belongings, and when dinner's over, you take the key back. Done.

OAuth works the same way. When an app wants to access something on your behalf (your Google contacts, your GitHub repos, your Spotify playlists), OAuth lets you hand it a limited-access key. The app gets just enough access to do its job, nothing more. And you can revoke that access anytime without changing your password.

Meet the Cast: The Four Actors

OAuth has four main players, and they map neatly onto the valet parking scenario:

  • Resource Owner (You) — The person who owns the car. You decide who gets access and what kind.
  • Client (The App) — The valet driver. They want access to your car, but only the access you grant them.
  • Authorization Server — The restaurant's valet desk. This is the trusted middleman that verifies your identity, asks what permissions you want to grant, and issues the valet key.
  • Resource Server — The parking garage where your car actually sits. It checks the valet key before letting anyone in.

In the real world, the Authorization Server and Resource Server are often run by the same company. Google's authorization server issues tokens, and Google's API servers (the resource servers) accept them. But conceptually, they're separate roles, and understanding them separately makes the whole flow click.

The Authorization Code Flow: Step by Step

The most common OAuth flow is called the Authorization Code flow, and it's what happens behind the scenes every time you click "Sign in with Google" or "Connect your GitHub account." Let's walk through it, keeping our valet analogy running alongside.

Step 1: The app sends you to the authorization server.

You visit a new app and click "Connect with Google." The app redirects your browser to Google's authorization page with a URL that looks something like this:

https://accounts.google.com/o/oauth2/auth?
  response_type=code&
  client_id=my-app-id&
  redirect_uri=https://myapp.com/callback&
  scope=contacts.readonly&
  state=random-csrf-token
Enter fullscreen mode Exit fullscreen mode

In our analogy, the valet has walked you over to the valet desk and said, "This person would like to give me access to drive their car — but only to drive it, not to open the trunk."

Step 2: You log in and approve.

Google shows you a consent screen: "This app wants to read your contacts. Allow?" You review the permissions and click "Allow."

This is you at the valet desk, confirming: "Yes, give them a valet key, but only for driving."

Step 3: Google sends you back with an authorization code.

Google redirects your browser back to the app's redirect_uri with a short-lived authorization code tucked into the URL:

https://myapp.com/callback?code=abc123&state=random-csrf-token
Enter fullscreen mode Exit fullscreen mode

This code is like a claim ticket — it proves you approved the request, but it's not the actual key yet. The valet desk hands you a slip of paper that says "approved for valet key #47."

Step 4: The app exchanges the code for tokens.

The app's backend server takes that authorization code and makes a direct, server-to-server request to Google (no browser involved):

POST https://oauth2.googleapis.com/token
  code=abc123&
  client_id=my-app-id&
  client_secret=my-app-secret&
  redirect_uri=https://myapp.com/callback&
  grant_type=authorization_code
Enter fullscreen mode Exit fullscreen mode

This is the crucial step. The app presents the claim ticket plus its own ID (the client_secret) to prove it's legitimately the app that requested access. Google verifies everything and hands back the actual tokens.

The valet takes the slip to the back office, shows their employee badge, and receives the actual valet key.

Step 5: The app uses the access token to call APIs.

Now the app can make API requests on your behalf:

GET https://www.googleapis.com/contacts/v1/people
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
Enter fullscreen mode Exit fullscreen mode

The valet is now driving your car — but only within the limits you set.

Tokens: Movie Tickets and Season Passes

When the app exchanges that authorization code, it gets back two tokens, and they serve very different purposes.

An access token is like a movie ticket. It gets you into one specific showing, it's valid for a limited time (usually 15 minutes to an hour), and once it expires, you need a new one. The app includes this token with every API request, and the resource server checks it before granting access.

A refresh token is more like a season pass to the cinema. It lasts much longer (days, weeks, sometimes indefinitely), and its only purpose is to get you new movie tickets without making you go through the whole purchase process again. When the access token expires, the app uses the refresh token to quietly request a new one in the background, so you don't get interrupted with another login screen.

This separation exists for security. Access tokens fly across the network with every API call, so keeping them short-lived limits the damage if one gets intercepted. Refresh tokens stay safely on the server, used only occasionally, and can be revoked independently.

Scopes: What Exactly Can This Key Open?

Remember how the valet key can start the engine but can't open the trunk? That's what scopes do in OAuth. They define the boundaries of what an app is allowed to do with your data.

When an app requests authorization, it specifies the scopes it needs. A contact-organizing app might request contacts.readonly, while an email client might ask for gmail.send. You'll see these on the consent screen, and you can usually decline if the permissions seem too broad.

Some common scope patterns you'll see in the wild:

  • read:user — read your profile info
  • repo — access your repositories
  • playlist-read-private — see your private playlists
  • email — access your email address

Scopes are your safety net. If an app that organizes photos is asking for permission to send emails on your behalf, that's a red flag.

Why the Redirect Dance?

You might be wondering why OAuth bothers with that whole redirect-to-Google-then-back-to-the-app routine. Why not just have the app ask Google directly?

The redirect exists because you need to authenticate directly with Google, not through the app. If the app handled your Google login, it could theoretically capture your password. By redirecting you to Google's actual login page, your credentials never touch the third-party app at all. The app only ever sees the authorization code and the resulting tokens — never your username, never your password.

The intermediate authorization code adds another layer of safety, too. It's single-use and expires in minutes, so even if someone intercepts it, they can't do much without the app's client_secret to complete the exchange.

A Quick Code Example

Here's what a basic OAuth integration looks like with GitHub using Express.js. Most of the heavy lifting is just URL construction and HTTP requests:

const express = require('express');
const axios = require('axios');
const app = express();

// Step 1: Redirect user to GitHub
app.get('/login', (req, res) => {
  const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${process.env.GITHUB_CLIENT_ID}&scope=read:user`;
  res.redirect(githubAuthUrl);
});

// Step 3: Handle the callback
app.get('/callback', async (req, res) => {
  const { code } = req.query;

  // Step 4: Exchange code for token
  const tokenResponse = await axios.post(
    'https://github.com/login/oauth/access_token',
    {
      client_id: process.env.GITHUB_CLIENT_ID,
      client_secret: process.env.GITHUB_CLIENT_SECRET,
      code,
    },
    { headers: { Accept: 'application/json' } }
  );

  const accessToken = tokenResponse.data.access_token;

  // Step 5: Use the token
  const userResponse = await axios.get('https://api.github.com/user', {
    headers: { Authorization: `Bearer ${accessToken}` },
  });

  res.json(userResponse.data);
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Most OAuth providers — Google, Facebook, Twitter, Spotify — follow this same pattern. The URLs change, the scopes change, but the dance is always the same.

PKCE: A Quick Note for Mobile and SPAs

The flow above works great when your app has a backend server that can securely store a client_secret. But what about mobile apps and single-page applications that run entirely in the browser? There's no safe place to hide a secret in JavaScript that anyone can inspect.

That's where PKCE (Proof Key for Code Exchange, pronounced "pixy") comes in. Instead of relying on a stored secret, the app generates a random string called a code_verifier for each authorization request, creates a hashed version called a code_challenge, and sends the challenge along with the initial request. When it exchanges the authorization code for tokens, it sends the original verifier. The authorization server checks that the verifier matches the challenge, proving the same app that started the flow is the one finishing it.

PKCE is now recommended for all OAuth clients, not just public ones. If you're implementing OAuth in 2025 and beyond, use PKCE.

The Big Misconception: OAuth Is Not Authentication

Here's something that trips up a lot of developers: OAuth is an authorization protocol, not an authentication protocol. It answers the question "what is this app allowed to do?" — not "who is this user?"

When you click "Sign in with Google," you're actually using OpenID Connect (OIDC), which is a thin identity layer built on top of OAuth 2.0. OIDC adds an id_token — a signed piece of data that tells the app who you are (your email, name, profile picture). Vanilla OAuth doesn't include this. An OAuth access token tells the resource server that someone approved access, but it doesn't necessarily tell the app who that someone is.

If you're building a "Sign in with..." feature, you want OIDC. If you're building an integration that accesses a user's data (importing contacts, posting to their timeline, reading their repos), you want OAuth. Often you'll use both together, and that's perfectly fine.

Cheat Sheet

Concept What It Does Analogy
OAuth 2.0 Grants limited access without sharing passwords Valet key system
Authorization Code Temporary proof of user consent Claim ticket from the valet desk
Access Token Short-lived key for API access Movie ticket (one showing)
Refresh Token Long-lived key to get new access tokens Season pass (gets new tickets)
Scopes Define what the app can and can't do Valet key restrictions (drive, but no trunk)
PKCE Secures the flow for public clients Sealed envelope — only the sender can open it
OIDC Adds identity (who you are) on top of OAuth The valet desk also checks your driver's license

Keep Going

OAuth is one of those topics that seems intimidating until you see the pattern, and then it shows up everywhere. The next time you click "Connect with GitHub" or "Allow access," you'll know exactly what's happening behind those redirects.

If you want to go deeper, the best next steps are to actually build a small OAuth integration yourself (the GitHub one above is a great starting point) and then read up on OpenID Connect to understand the authentication side. The OAuth 2.0 RFC (6749) is surprisingly readable for a spec document, and the OAuth.net playground lets you step through the flow interactively.

Got questions or a favorite OAuth analogy of your own? Drop it in the comments — these concepts click differently for everyone, and the best explanations come from the community.

Top comments (0)