The Authorization Code Grant in OAuth 2.0

The Authorization Code Grant in OAuth 2.0

A complete, beginner-friendly walkthrough of how apps ask for your permission, get a temporary code, and exchange it for real access — without ever seeing your password.

Imagine you are staying at a hotel. You do not get a master key that opens every room in the building. Instead, the front desk checks who you are, hands you a small paper voucher, and you take that voucher to a separate machine that presses your actual room key. The voucher itself opens nothing. It only proves that the front desk already approved you, and it can be exchanged for the real key just once. The Authorization Code Grant in OAuth 2.0 works almost exactly like this voucher system. It is the most widely used way for an application to get permission to act on your behalf on another service, such as logging into a website using your Google or GitHub account, without that application ever touching your password. This tutorial walks through every part of that voucher system, piece by piece, until the whole picture is clear.

1What Is OAuth 2.0 and Why Does It Exist?

Before understanding the Authorization Code Grant, it helps to understand the problem OAuth 2.0 was built to solve.

OAuth 2.0 is an authorization framework. That word “authorization” is important, and it is different from “authentication.” Authentication answers the question “who are you?” Authorization answers the question “what are you allowed to do?” OAuth 2.0 is entirely about the second question. It defines a standard way for one application to be granted limited access to a user’s data or account on another application, without the user having to hand over their username and password to that first application.

Simple Analogy

Think about a valet parking service. You do not give the valet your house keys and your car’s title deed. You give them a single valet key that only starts the engine and opens the driver’s door — it cannot open your glove box or your trunk. OAuth 2.0 is the system that mints these limited-purpose “valet keys” for software instead of cars.

Before OAuth existed, if a photo-printing website wanted to fetch your photos from another photo-storage service, the only option was for you to type your photo-storage username and password directly into the printing website. That website could now do absolutely anything your account could do — delete photos, change your password, read your private messages — because it was holding your full master key. This pattern was called the “password anti-pattern,” and it was both dangerous and hard to undo. If you wanted to revoke access later, your only option was to change your master password everywhere.

!
The Problem OAuth Fixes

Sharing your real password with a third-party app gives that app unlimited, permanent power over your account, with no easy way to switch it off later without changing your password for everyone.

OAuth 2.0 solves this by introducing a middleman step. Instead of a password, the third-party app receives a special, limited token. This token can be scoped down to very specific permissions (for example, “read my photos” but not “delete my photos”), it can expire on its own, and it can be revoked individually without touching your actual password. The Authorization Code Grant is simply the specific, most secure recipe within OAuth 2.0 for producing that token when a real human is present to approve the request through a web browser.

2Meet the Four Actors

Every OAuth 2.0 conversation involves exactly four roles. Learning their names now makes every later chapter much easier to follow.

OAuth 2.0 specifications always describe the same four participants, no matter which company is running the system. Once you can name each one and their job, the entire flow becomes a simple story about these four characters passing messages to each other.

Actor 1

Resource Owner

This is you, the human being. You own the data (your photos, your contacts, your profile) and only you can approve who gets to touch it.

Actor 2

Client

The application that wants access to your data on your behalf — for example, a fitness app that wants to read your calendar so it can schedule workouts.

Actor 3

Authorization Server

The security office. It checks who you are, shows you the consent screen, and issues codes and tokens. Google Accounts and GitHub’s login page are examples.

Actor 4

Resource Server

The place where your actual data lives, such as the Google Calendar API or the GitHub Repositories API. It only responds to requests that carry a valid token.

Simple Analogy

Picture an office building. You (the Resource Owner) work there. A delivery courier (the Client) wants to drop a package on the fifth floor. The building’s front-desk security guard (the Authorization Server) checks the courier’s ID and issues a temporary visitor badge. The fifth-floor office (the Resource Server) only lets people in if they are wearing a valid badge, and the badge only opens the floors it was programmed for.

In many real systems, the Authorization Server and the Resource Server are operated by the same company and even share infrastructure, but they are still treated as two logically separate jobs. The Authorization Server’s entire responsibility ends the moment it hands out a token. From then on, the Resource Server is the one deciding whether that token is still valid and what it is allowed to unlock.

3What Exactly Is the Authorization Code Grant?

Now that the actors are known, this chapter names the specific pattern this tutorial is about.

OAuth 2.0 actually defines several different “grant types,” which are simply different recipes for obtaining a token, each suited to a different kind of situation. The Authorization Code Grant is the recipe used whenever a real user is sitting in front of a web browser and can be asked, face to face, “do you approve this?” It is considered the flagship, most secure, and most widely recommended grant type in the entire OAuth 2.0 family.

The defining feature of this grant is the two-step exchange. First, the Client redirects the user’s browser to the Authorization Server, the user logs in and approves the request, and the Authorization Server sends back a short, one-time-use string called the authorization code. Second, and critically, the Client then takes that code and, from its own backend server (not through the user’s browser), exchanges it for the real access token. This second step happens over a private, server-to-server channel that the user’s browser never sees.

Simple Analogy

Recall the hotel voucher from the introduction. Step one is getting the voucher at the front desk in full view of everyone in the lobby. Step two is walking to a back room, out of public sight, and trading that voucher for the actual room key. Anyone who saw you holding the voucher in the lobby cannot use it themselves, because only the back-room machine — which also demands a secret staff badge — will accept it.

i
Why Two Steps Instead of One?

Splitting the process into a public “front lobby” step and a private “back room” step means the sensitive access token itself is never exposed in the browser’s address bar, browser history, or server logs — only the short-lived, single-use code is.

This two-step design is the single most important idea in this entire tutorial. Nearly every security benefit of the Authorization Code Grant traces back to this simple decision to separate “getting approved” from “getting the real key.”

4The Building Blocks You Will See Everywhere

A handful of technical terms appear again and again in every OAuth conversation. Defining them once here will save confusion later.

Term

Client ID

A public, non-secret identifier that names which application is making the request — similar to a shop’s public storefront name.

Term

Client Secret

A private password known only to the Client’s backend server, used to prove during the token exchange that the request truly comes from that registered application.

Term

Redirect URI

The exact web address the Authorization Server is allowed to send the user’s browser back to after approval, pre-registered in advance to stop attackers from redirecting the code elsewhere.

Term

Scope

A list of specific permissions being requested, such as “read your email address” or “read your calendar,” so access can be narrowed down instead of being all-or-nothing.

Term

Authorization Code

The short-lived, one-time-use voucher issued after the user approves the request, typically valid for less than a minute and usable exactly once.

Term

Access Token

The real, limited-purpose key. It is presented to the Resource Server on every request and proves the Client has permission to act, within its granted scope.

Term

Refresh Token

A longer-lived credential that lets the Client quietly obtain a brand-new access token later, without asking the user to log in and approve everything again.

Term

Consent Screen

The page the Authorization Server shows the user, listing exactly what the Client is asking to access, so the human can approve or deny it in plain language.

Two web addresses matter enormously in this whole process, and it is worth naming them clearly. The Authorization Endpoint is the page a browser is sent to first, where the login and consent screen live. The Token Endpoint is a completely separate, backend-only address where the authorization code gets exchanged for tokens. Confusing these two endpoints is one of the most common beginner mistakes, so remember: the Authorization Endpoint talks to browsers, and the Token Endpoint talks only to servers.

5The Flow, Step by Step

This is the heart of the tutorial: the exact sequence of events, told as a story with numbered steps.

1

User Clicks “Log In With…”

The user visits the Client application and clicks a button such as “Log in with Google.” This is the moment the whole process begins.

2

Client Redirects to the Authorization Server

The Client’s browser is redirected to the Authorization Endpoint, carrying its Client ID, the requested scopes, the registered Redirect URI, and a random one-time value called “state” used to prevent forgery.

3

User Logs In (If Not Already)

The Authorization Server checks whether the user has an active session. If not, it shows a familiar login form asking for a username and password, or a passkey, or a fingerprint — this login happens entirely on the Authorization Server’s own page, never inside the Client’s app.

4

Consent Screen Is Shown

The user sees a clear list of what the Client is asking for, such as “This app wants to view your profile and read your calendar,” and chooses Allow or Deny.

5

Authorization Server Issues the Code

If approved, the Authorization Server redirects the browser back to the Client’s pre-registered Redirect URI, attaching a short, random authorization code as part of the address, along with the same “state” value from step two.

6

Client Verifies the “State” Value

The Client checks that the returned “state” matches exactly what it originally sent, which confirms this response belongs to a request it actually started and was not forged by someone else.

7

Backend Exchanges the Code for Tokens

The Client’s own server, not the browser, sends the authorization code, its Client ID, and its Client Secret directly to the Token Endpoint over a private, encrypted server-to-server connection.

8

Authorization Server Responds With Tokens

After confirming the code is genuine, unused, and not expired, the Authorization Server replies with an access token and, often, a refresh token, sent directly to the Client’s backend where the user’s browser cannot see them.

9

Client Calls the Resource Server

Whenever the Client needs the user’s data, it attaches the access token to its request to the Resource Server, which checks the token’s validity and scope before answering.

i
Notice the Split

Steps one through six happen entirely in the user’s browser and are, by design, visible to the user and potentially to anyone watching browser history. Steps seven and eight happen on a private server-to-server channel the browser never touches. This is exactly the “front lobby vs. back room” split described earlier.

6Seeing the Whole Flow as a Diagram

Reading nine numbered steps in a row can feel like a lot. This diagram shows the same story as a picture.

sequenceDiagram
    participant U as User (Browser)
    participant C as Client App
    participant A as Authorization Server
    participant R as Resource Server

    U->>C: 1. Click "Log in with..."
    C->>U: 2. Redirect to Authorization Server
    U->>A: 3. Load login + consent page
    A->>U: 4. Show login form and consent screen
    U->>A: 5. Approve access
    A->>U: 6. Redirect back with authorization code
    U->>C: 7. Deliver code to Client's Redirect URI
    C->>A: 8. Exchange code + client secret (server-to-server)
    A->>C: 9. Return access token and refresh token
    C->>R: 10. Call API with access token
    R->>C: 11. Return protected data
        
FIG 1 — The complete Authorization Code Grant sequence, from click to protected data.

Notice how the arrows change color of “ownership” partway through the diagram. Everything up through the redirect carrying the authorization code happens in full view of the user’s browser. Everything from the code exchange onward happens directly between the Client’s own backend server and the Authorization Server, with the browser stepping out of the conversation entirely. This is not an accident of implementation — it is the entire security model of the grant.

Simple Analogy

It is similar to ordering food through a restaurant’s front counter. You place your order and get a paper receipt with an order number (the code) — that receipt alone does not give you the food. You then hand that receipt to a kitchen staff member through a separate window, and only then do you receive the actual meal (the access token).

7Why Not Just Hand Over the Access Token Directly?

A natural question at this point is: why bother with an intermediate code at all? Why not send the access token straight back in step six?

This exact question has a historical answer. An earlier, now-discouraged OAuth pattern called the Implicit Grant did exactly that — it returned the access token directly in the browser’s address bar, with no code and no back-room exchange step. It turned out to be significantly less safe, and understanding why makes the Authorization Code Grant’s design much clearer.

Problems With Returning a Token Directly

  • Tokens appearing in the browser’s address bar get logged by web servers, browser history, and proxy servers along the way.
  • Browser extensions, other scripts on the page, or a shared/public computer’s next user could potentially read it from history.
  • There is no opportunity to prove the request truly came from the legitimate Client’s backend, since nothing private like a Client Secret is ever checked.
  • If the token leaks, there is no separate, short-lived “code” layer to contain the damage — the real key is already loose.

What the Extra Code Step Buys You

  • The code that appears in the browser is short-lived (often under sixty seconds) and can be used exactly once.
  • Even if the code is somehow intercepted, it is useless without the Client Secret, which never leaves the Client’s backend server.
  • The real access token is transmitted only once, over an encrypted, private, server-to-server channel that browser history never records.
  • The Authorization Server gets a second checkpoint to confirm the exchange really is coming from the registered Client application.
“An authorization code is valuable for about a minute and useless without a secret. An access token is valuable for as long as it lives and useless without nothing at all.”

Because of these weaknesses, the Implicit Grant has since been formally deprecated in current OAuth 2.0 security guidance, and the Authorization Code Grant — especially when combined with the extra protection covered in the next chapter — is now the recommended approach for essentially every situation involving a browser.

8PKCE: Extra Protection for Apps That Cannot Keep Secrets

Client Secrets work well for backend web servers, but what about a mobile app or a single-page JavaScript app, which cannot truly hide a secret from a determined user?

A traditional web application, such as one built with a server-rendered backend, can store its Client Secret safely inside server code that the public never sees. But a mobile app installed on someone’s phone, or a JavaScript application running entirely inside a browser, cannot hide a secret the same way — a sufficiently determined person can decompile the app or inspect the browser’s network traffic and extract any secret hardcoded inside it. These are called “public clients,” as opposed to “confidential clients” that can keep a secret safely.

Simple Analogy

Giving a Client Secret to a mobile app is like writing your house alarm code on a sticky note and taping it inside a briefcase you then hand to a stranger, asking them not to look. A confidential backend server is more like a locked safe in a building only your own staff can enter — the secret genuinely stays hidden.

To protect public clients, the OAuth community introduced an extension called PKCE, pronounced “pixy,” short for Proof Key for Code Exchange. Instead of relying on a fixed, hardcoded secret, the Client generates a brand-new random secret value for every single login attempt, called a “code verifier.” It then creates a scrambled, one-way version of that value, called the “code challenge,” and sends only the scrambled version along with the very first redirect in step two. Later, during the code exchange in step seven, the Client reveals the original, unscrambled code verifier. The Authorization Server scrambles it the same way and checks that the two match before releasing any tokens.

flowchart LR
    A["Client generates random\ncode verifier"] --> B["Client scrambles it into\na code challenge"]
    B --> C["Code challenge sent with\nthe authorization request"]
    C --> D["Authorization Server\nstores the challenge"]
    D --> E["Client later sends the\noriginal code verifier"]
    E --> F["Server re-scrambles it and\ncompares to stored challenge"]
    F --> G{Match?}
    G -->|Yes| H["Tokens released"]
    G -->|No| I["Request rejected"]
        
FIG 2 — How PKCE proves the code exchange request comes from the same app that started the login, without needing a stored secret.
i
Why This Helps

Even if an attacker somehow intercepts the authorization code from step six, they cannot exchange it for tokens because they do not know the one-time, randomly generated code verifier that only the genuine app created and kept in memory for that single attempt.

Current security guidance now recommends using PKCE not only for mobile and single-page apps, but for every Authorization Code Grant implementation, including traditional backend web servers, simply as an extra layer of defense at essentially no cost.

9Security Considerations and Common Threats

Understanding the flow is only half the picture. Understanding what can go wrong is equally important.

Threat

Authorization Code Interception

An attacker on the same device tries to capture the code as it travels back to the Client. PKCE neutralizes this even if it succeeds.

Threat

Redirect URI Manipulation

An attacker tries to trick the Authorization Server into sending the code to a different, attacker-controlled address. Pre-registering exact Redirect URIs blocks this.

Threat

Cross-Site Request Forgery

An attacker tries to trick a victim’s browser into completing part of the flow on the attacker’s behalf. The random “state” value defeats this by ensuring the response matches a request the Client genuinely started.

Threat

Token Leakage in Logs

Access tokens accidentally written into server logs, error messages, or analytics tools can be stolen later. Careful logging hygiene and short token lifetimes limit the damage.

!
Always Use HTTPS

Every single step of the Authorization Code Grant must travel over encrypted HTTPS connections. Without encryption, the authorization code, the client secret, and the tokens could all be read by anyone watching the network traffic in between.

Access tokens are deliberately designed to be short-lived, often expiring within an hour or less. This is intentional: if a token does leak, the window during which it remains useful to an attacker is kept as small as possible. Refresh tokens, which live much longer, are correspondingly treated with much greater care — they are usually kept only on trusted backend servers and never exposed to browser-side JavaScript, precisely because their longer lifespan makes them a far more valuable target if stolen.

10Advantages and Trade-offs

No design is free of trade-offs. Here is an honest look at what the Authorization Code Grant gets right, and what it costs.

Advantages

  • The real access token is never exposed to the browser’s address bar or history.
  • A stolen authorization code is useless within seconds and without the matching secret or PKCE verifier.
  • Scopes let users see and approve exactly what is being shared, in plain language.
  • Refresh tokens allow long-lived access without repeatedly re-prompting the user to log in.
  • Access can be revoked individually per application, without changing the user’s actual password.
  • It works consistently across web servers, mobile apps, and single-page apps when combined with PKCE.

Disadvantages / Trade-offs

  • It requires a full browser redirect, which is a poor fit for devices without a browser, such as smart TVs (a different grant type exists for those).
  • It introduces more moving parts — an extra network round trip, a code, and careful Redirect URI configuration — compared to simpler, less secure flows.
  • Developers must correctly implement the “state” check and, ideally, PKCE, or some of the protection is lost.
  • It depends entirely on the Authorization Server correctly enforcing short code lifetimes and one-time use.

In practice, these trade-offs are considered well worth it. The small amount of extra implementation complexity buys a large amount of real-world security, which is exactly why nearly every major platform — from social networks to banks to developer tools — has standardized on this grant as their default choice whenever a human is present with a browser.

11Best Practices and Common Mistakes

Many real-world OAuth security incidents trace back to a small number of repeated mistakes. Knowing them in advance helps you avoid them.

ANTI-PATTERN-01 Avoid
Problem

Registering a loose or wildcard Redirect URI, such as anything under a broad domain, instead of one exact, specific address.

Why It’s Harmful

A loosely matched Redirect URI gives an attacker room to register a lookalike page under the same broad pattern and trick the Authorization Server into sending the code there instead.

Correct Approach

Register the exact, complete Redirect URI, with no wildcards, and reject anything that does not match it character for character.

ANTI-PATTERN-02 Avoid
Problem

Skipping the “state” parameter check because it feels like an optional extra step.

Why It’s Harmful

Without verifying “state,” the Client cannot tell whether the incoming authorization code truly belongs to a login attempt it started, opening the door to cross-site request forgery attacks.

Correct Approach

Always generate a fresh random “state” value per request, store it briefly, and reject any callback whose returned “state” does not match exactly.

ANTI-PATTERN-03 Avoid
Problem

Storing access tokens or refresh tokens in places accessible to browser-side JavaScript, such as ordinary local storage, in a single-page application.

Why It’s Harmful

Any malicious script that manages to run on the page, through a vulnerability elsewhere, can then simply read the tokens directly out of storage and steal them.

Correct Approach

Keep long-lived tokens on a trusted backend server whenever possible, and rely on secure, restricted cookies rather than script-accessible storage for anything sensitive.

ANTI-PATTERN-04 Avoid
Problem

Requesting far broader scopes than the application actually needs, “just in case.”

Why It’s Harmful

Overly broad scopes mean that if the token is ever compromised, the blast radius of what an attacker can do with it is much larger than necessary, and it also erodes user trust when the consent screen looks excessive.

Correct Approach

Request only the specific scopes the feature currently being built actually requires, and request additional scopes later, incrementally, only when a new feature genuinely needs them.

12Real-World Examples You Have Probably Already Used

The Authorization Code Grant is not an abstract academic idea — it quietly runs behind buttons you click every day.

“Sign in with Google” Buttons

When a website offers to let you log in using your existing Google account, it is almost always running the Authorization Code Grant behind the scenes, redirecting you to Google’s own login and consent page rather than asking you to type a Google password directly into the third-party site.

Connecting a Calendar App to Your Work Calendar

A scheduling tool that asks to “connect your calendar” is requesting a token with a narrow scope, such as read-only calendar access, so it can suggest meeting times without being able to read your email or delete your account.

Developer Tools Connecting to GitHub

A continuous integration service that needs to read your code repositories typically uses this same flow, redirecting you to GitHub’s consent screen, which lists precisely which repositories and permissions it is requesting.

Payment and Banking Aggregators

Personal finance apps that show balances from multiple banks in one place rely on authorization flows built on the same core pattern, so the finance app never sees your actual banking password, only a scoped, revocable token.

In every one of these examples, the same underlying shape repeats: a redirect to a trusted party’s own login page, a clear consent screen naming exactly what is being requested, a short-lived code, and a private backend exchange for the real token. Once you recognize this shape once, you will start noticing it everywhere.

13How It Compares to Other OAuth 2.0 Grant Types

The Authorization Code Grant is not the only recipe in OAuth 2.0. Seeing it alongside its siblings helps clarify when it is the right choice.

Grant TypeTypical Use CaseInvolves a Browser?Current Recommendation
Authorization Code (+ PKCE)Web apps, mobile apps, single-page apps with a real user presentYesRecommended default for almost everything
Client CredentialsServer-to-server communication with no individual user involvedNoRecommended for machine-to-machine access
Device CodeDevices without a convenient browser, such as smart TVs or set-top boxesIndirectly, via a second deviceRecommended for input-constrained devices
ImplicitHistorically used for single-page apps before PKCE existedYesDeprecated; replaced by Authorization Code with PKCE
Resource Owner Password CredentialsLegacy migration scenarios where the app is fully trusted by the userNoDeprecated; avoid in new designs

The pattern in this table is worth noticing: OAuth 2.0’s guidance has steadily moved away from any grant type that either exposes a token directly in a browser or requires typing a real password into a third-party application, and steadily toward the Authorization Code Grant with PKCE as the safe, general-purpose default whenever a human user is involved.

14Frequently Asked Questions

Q1Does the Client ever see the user’s actual password?

No. The password, or any other login method such as a fingerprint or passkey, is entered directly on the Authorization Server’s own login page, which the Client never has access to. The Client only ever receives a code and, later, tokens.

Q2What happens if the authorization code is used twice?

A well-implemented Authorization Server rejects the second attempt outright, and many implementations go further by immediately revoking any tokens that were already issued from that code, treating reuse as a strong signal of an attack in progress.

Q3Why does the access token expire so quickly?

Short lifetimes limit how much damage a leaked token can do. Instead of forcing the user to log in again every time it expires, the Client can quietly use a refresh token to obtain a new access token in the background.

Q4Is PKCE only needed for mobile apps?

PKCE was originally designed for public clients like mobile and single-page apps that cannot keep a secret safely, but current security guidance recommends using it for every Authorization Code Grant implementation, including traditional backend web servers, as a low-cost extra layer of defense.

Q5Can a user see and revoke which apps have access?

Yes. Most major Authorization Servers provide an account settings page listing every application that has been granted access, along with the exact scopes approved, and a button to revoke access for any individual app at any time.

Q6What is the difference between an access token and an ID token?

An access token proves permission to call an API on the user’s behalf. An ID token, which comes from a related standard built on top of OAuth 2.0 called OpenID Connect, instead proves who the user actually is, and is used specifically for login and identity rather than data access.

Q7Why is a Redirect URI pre-registered instead of sent freely each time?

Pre-registration lets the Authorization Server refuse to send an authorization code anywhere except an address the Client owner has already proven control over, which closes off a whole category of redirection-based attacks.

15Summary and Key Takeaways

The Authorization Code Grant is, at its core, a carefully engineered two-step handshake. A user approves a request in full view of their own browser, receives a short-lived, one-time voucher, and that voucher is then quietly exchanged for the real access credential over a private, backend-only channel the browser never sees. Every design choice in the flow — the short code lifetime, the “state” check, the pre-registered Redirect URI, and the optional but increasingly essential PKCE extension — exists to close a specific, real-world attack that earlier, simpler approaches suffered from. Once you can see the flow as this “front lobby, then back room” story, the entire specification stops feeling like an arbitrary list of steps and starts feeling like a logical, defensible system.

Key Takeaways

  • OAuth 2.0 is about authorization, not authentication — it answers “what can this app do,” not “who are you.”
  • Four actors run the show — the Resource Owner, the Client, the Authorization Server, and the Resource Server each have one clear job.
  • The grant is a two-step exchange — a public code handed to the browser, then a private server-to-server swap for the real token.
  • The “state” parameter blocks forgery — always generate one, send it, and verify it matches on the way back.
  • PKCE removes the need for a hidden secret — a fresh, random code verifier proves the exchange came from the same app that started it.
  • Tokens should be short-lived and scoped narrowly — this limits the damage if one is ever leaked.
  • This is the recommended default — for web apps, mobile apps, and single-page apps alike, whenever a real user is present with a browser.