What Is It?
OAuth and JWT are two different things that get tangled together because they're often used in combination.
OAuth (Open Authorization) is a protocol for granting one application access to your account in another application — without sharing your password. When you click "Sign in with Google" and Google shows you a consent screen saying "App X wants to read your calendar", that's OAuth. It's fundamentally about authorisation (what can this app access?) not authentication (who is this user?). The confusion is so common there's a whole extension called OIDC (OpenID Connect) that adds authentication on top of OAuth.
JWT (JSON Web Token) is a token format — a standard way to encode and sign data so it can be verified without a database lookup. A JWT is just a base64-encoded JSON object with a cryptographic signature. JWTs are used in OAuth, but they're also used in many non-OAuth authentication systems. They're separate concepts.
Understanding both means you understand nearly all of the "Auth" plumbing in modern apps — why "Login with Google" works the way it does, what your auth library is actually doing when it issues tokens, and why your access token is a weird string full of dots.
How It Actually Works
The OAuth Flow
OAuth 2.0 has four main "grant types". The one you'll encounter most in web apps is the Authorization Code Flow:
User's browser Your App Google (Auth Server)
| | |
|── clicks "Login ───►| |
| with Google" | |
| |── redirect ────────────►|
| | to Google with: |
| | client_id, |
| | redirect_uri, |
| | scope, |
| | state (CSRF token) |
| | |
|◄────────────────────|── Google shows ─────────|
| Google login & | consent screen |
| consent screen | |
| | |
|── user approves ───────────────────────────► |
| | |
|◄────────────────────────── redirect ─────────|
| to your | redirect_uri with: |
| redirect_uri | code=abc123 |
| with ?code=abc123 | state=xyz |
| | |
|── send code ────────►| |
| to your app |── exchange code ────────►|
| | for tokens |
| |◄── access_token, ───────|
| | refresh_token, |
| | id_token (OIDC) |
The key insight: the code exchanged at the end is single-use and short-lived. It goes from Google to your server directly (not via the browser URL bar where it could be intercepted). Your server exchanges it for tokens in a server-to-server call that includes your client_secret — which only your server knows.
The Tokens
After the flow, your app has:
- Access Token: Short-lived (1 hour). Use this to call Google APIs on the user's behalf (read their calendar, send email, etc.)
- Refresh Token: Long-lived. Use this to get a new access token when the old one expires, without making the user log in again.
- ID Token (when using OIDC): A JWT containing the user's identity. This is the authentication part — who this person is.
What's Inside a JWT
A JWT has three parts, separated by dots: header.payload.signature
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoiamFtaWVAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MTQxNDI0MDAsImV4cCI6MTcxNDE0NjAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Each part is base64-encoded. Decode them:
Header:
{
"alg": "RS256",
"typ": "JWT"
}
Payload:
{
"sub": "user_123",
"email": "jamie@example.com",
"role": "admin",
"iat": 1714142400,
"exp": 1714146000
}
Signature:
The header and payload are signed using the algorithm specified (RS256 = RSA with SHA-256). The signature proves the token hasn't been tampered with.
If someone changes the payload (e.g., changes "role": "admin" to their advantage), the signature won't match anymore. Without the signing key, you can't forge a valid signature.
The payload is base64-encoded, not encrypted. Anyone can decode a JWT and read the payload. Never put sensitive data (passwords, full credit card numbers) in a JWT.
Verifying a JWT
The receiving server:
- Splits the token at the dots
- Takes the header + payload + the signing key
- Recomputes the signature
- Checks if it matches
- Checks
exp(expiry) — is it in the past? - Checks
iss(issuer) — is this from who I expect? - If all pass: trusts the payload, extracts
subas the user ID
No database lookup needed. This is the "stateless" advantage — any server with the signing key can verify any token independently.
OAuth vs OIDC
OAuth 2.0 solves authorisation: "Can this app access my Google Calendar?"
OpenID Connect (OIDC) adds an authentication layer on top of OAuth: "Who is this user?"
OIDC is why "Sign in with Google" returns an id_token (a JWT with the user's email, name, profile picture) alongside the access token. The ID token is the authentication assertion. The access token is the authorisation credential.
When an auth library handles "Sign in with Google" for you, it's running the OAuth 2.0 Authorization Code Flow + OIDC, extracting the ID token, verifying it, and creating a session for your app.
The Jargon Decoded
- OAuth 2.0 — A protocol for delegated authorisation. "Let app X access my data in service Y on my behalf." Not a login protocol.
- OIDC (OpenID Connect) — An authentication layer built on top of OAuth 2.0. Adds the concept of an ID token and a
/userinfoendpoint. This is what makes OAuth usable for "Sign in with Google". - Access Token — A short-lived credential (often a JWT) for calling APIs. Scoped to specific permissions. Expires in minutes to hours.
- Refresh Token — A long-lived credential for obtaining new access tokens without re-authentication. Stored securely server-side. Can be revoked.
- Client ID / Client Secret — Your app's credentials with Google (or any OAuth provider). The client ID is public; the client secret is private and must never be exposed client-side.
- Scope — What permissions you're requesting.
email profilereads basic info.https://www.googleapis.com/auth/calendarreads calendar data. Users see the scopes on the consent screen. - Authorization Code — The one-time short-lived code returned after user consent, exchanged server-side for tokens. Single use.
- PKCE (Proof Key for Code Exchange) — An extension for public clients (SPAs, mobile apps) that can't store a client secret securely. Generates a code challenge/verifier pair to prevent authorization code interception.
- JWT (JSON Web Token) — A base64-encoded, signed token with a header, payload, and signature. Self-contained — no database lookup needed to verify.
- Claims — The key-value pairs in a JWT payload.
sub(subject/user ID),exp(expiry),iss(issuer),iat(issued at) are standard claims.
Why This Matters When You're Building
Every auth library you use implements this under the hood. NextAuth, Clerk, Auth0, Supabase Auth — they all run OAuth + OIDC flows. When you configure "Google provider" in NextAuth, you're setting up the authorization code flow. When your session has user data, it came from decoding the ID token.
The client secret is critical. Your OAuth client_secret is what lets your server exchange authorization codes for tokens. Never expose this in frontend JavaScript, browser extensions, or mobile apps. Only server-to-server. The authorization code flow exists specifically so this exchange happens server-side.
Token expiry causes UX problems if ignored. Access tokens expire. If your app calls an API with an expired access token, it gets 401. You need to handle this: either refresh silently, or redirect to login. Auth libraries handle this for you — but you need to understand why a user might get logged out mid-session.
JWTs are not sessions. You can't invalidate a JWT before its expiry (unless you maintain a revocation list, which adds state). If a user's JWT is stolen or you need to force-logout all users after a security breach, short-lived tokens + server-side revocation lists are the answer.
What To Tell The AI
"I'm adding Google OAuth to my Next.js app with NextAuth. Walk me through the exact setup: creating OAuth credentials in Google Cloud Console (what redirect URIs to set), the NextAuth configuration, and how the callback URL works. Explain what data I get back and how to access it."
"My app issues JWTs to users after login. I need to add a 'revoke all sessions' feature (for security incidents). Explain why I can't just delete JWTs and walk me through two approaches: short-lived tokens with a revocation list, and reference tokens. Implement the revocation list approach."
"Decode and explain this JWT: [paste token]. What do the standard claims mean, what's in the payload, and what algorithm was used to sign it? Is the payload encrypted or just encoded?"
"I'm building a mobile app that uses OAuth to connect users' Google Calendar. Explain why I can't use the standard authorization code flow from a mobile app and show me how to implement PKCE instead."
"I want to let users of my app connect their Slack workspace so I can post notifications on their behalf. Walk me through the Slack OAuth flow: what credentials I need, the redirect flow, how to store the access token, and how to refresh it."
Common Misconceptions
"OAuth is a login protocol." OAuth is an authorisation protocol. It grants access to resources. The reason it's used for "Sign in with Google" is that OpenID Connect added a thin authentication layer on top. Without OIDC, OAuth tells you "this user authorised access to their Google account" — not "this user is google-user@gmail.com".
"The access token is secret." Access tokens are credentials that should be protected, but they're less sensitive than refresh tokens or client secrets. Access tokens expire in hours. Refresh tokens last weeks/months and can be used to generate unlimited new access tokens. Protect both, but refresh tokens are the higher-value target.
"A JWT is encrypted." The payload is base64-encoded, which is reversible by anyone. Base64 is an encoding, not encryption. Don't put sensitive data in JWTs. If you need encrypted JWTs, that's JWE (JSON Web Encryption) — a different standard entirely.
"I should build OAuth from scratch." Building a correct OAuth implementation requires handling state parameters (CSRF prevention), PKCE, token refresh, scope handling, and security edge cases. Use a battle-tested library (NextAuth, Passport.js, the provider's official SDK). The spec is complex and the security implications of getting it wrong are severe.
Sources
- OAuth 2.0 Simplified (Aaron Parecki) — The clearest written guide to OAuth 2.0, by one of the spec authors
- jwt.io — Interactive JWT decoder/encoder + introduction to the standard
- OpenID Connect Explained — Connect2id — Thorough explanation of how OIDC extends OAuth for authentication
- Auth0 — OAuth 2.0 and OpenID Connect — Practical explanation with flow diagrams
- RFC 6749 — The OAuth 2.0 Authorization Framework — The spec itself; the abstract and introduction are worth reading