October 4, 2026 · Yunus Emre Vurgun
Which OAuth2 Grant Type Should I Use?
OAuth2 is an authorization framework that lets users grant third-party apps limited access to their accounts without sharing passwords, and the grant type — now usually called the flow — defines how the app obtains that access. For most apps built today the answer is simple: use Authorization Code with PKCE. The right choice depends on what kind of client you are building — a browser app, a mobile app, a server backend, or a device with no browser — so this guide compares every grant type and tells you exactly when to use each.
How OAuth2 works in one minute
OAuth2 separates four roles. The resource owner is the user. The client is your app, which wants access. The authorization server verifies the user and issues tokens — think "Sign in with Google". The resource server hosts the protected API and accepts those tokens. Your app never sees the user's password; instead it receives an access token with a limited scope (such as read:email) and a limited lifetime, plus optionally a refresh token to get new access tokens later.
The grant type is simply the choreography by which the client earns its tokens. A browser app redirects the user to the authorization server for approval; a backend service authenticates with its own client secret; a smart TV shows a code the user types into their phone. Each flow exists because each client type has different abilities: some can keep secrets, some can open browsers, and some can do neither. Confusingly, OAuth2 is about authorization (what the app may access), not authentication (who the user is) — OpenID Connect is the thin identity layer on top that adds login. For the broader picture of proving identity, see the authentication factors reference.
The grant types compared
There are five flows you will meet in practice. This table is the short version; the sections below explain the reasoning:
| Grant type | Best for | How it works |
|---|---|---|
Authorization code + PKCE | SPAs, mobile apps, regular web apps — the default | User approves in browser; app exchanges code plus verifier for tokens. |
Client credentials | Server-to-server, cron jobs, microservices | App authenticates with its own id and secret; no user involved. |
Device authorization | TVs, CLIs, IoT with no browser | Device shows a code; user approves on phone; device polls for tokens. |
Implicit (legacy) | Nothing new — deprecated | Tokens returned directly in the redirect URL fragment. |
Resource owner password (legacy) | Nothing new — deprecated | App collects the user's password directly. Avoid. |
The pattern to notice: modern OAuth2 never puts tokens in URLs and never asks the user to type their password into a third-party app. Both legacy flows violate one of those rules, which is why the current best-practice guidance retires them. A typical token exchange for the authorization code flow is a single server-to-server POST:
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=AUTH_CODE_HERE
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&client_id=YOUR_CLIENT_ID&code_verifier=RANDOM_VERIFIER
Authorization Code with PKCE: the default choice
The authorization code flow has two steps. First, your app redirects the user to the authorization server, which authenticates them and asks for consent ("Allow MyApp to read your email?"). On approval, the server redirects back to your app with a short-lived, single-use authorization code. Second, your app exchanges that code for tokens over a direct back-channel call the browser never sees.
PKCE (Proof Key for Code Exchange, pronounced "pixy") hardens the exchange against code interception. Your app generates a random code_verifier, sends its SHA-256 hash (code_challenge) with the initial redirect, and presents the original verifier when redeeming the code. Even if an attacker steals the authorization code from the redirect, it is useless without the verifier, which never traveled through the browser. PKCE was designed for public clients that cannot keep a secret — SPAs and mobile apps — but current guidance recommends it for all clients, including confidential web apps. If you take one thing from this article, it is this: start with Authorization Code + PKCE unless you have a concrete reason not to.
Which OAuth2 flow should I use for my app type?
Building a single-page app or mobile app? Use Authorization Code + PKCE, with tokens held in memory and refresh handled via a backend-for-frontend or a secure cookie where possible. Building a traditional server-rendered web app? Use Authorization Code with PKCE too — your server is a confidential client that can additionally authenticate with a client secret, giving you both layers of protection.
Building a backend service, cron job, or service-to-service call with no user present? Use client credentials: the app proves its own identity with a client id and secret (or better, a signed JWT assertion) and receives a token scoped to itself. Building for a TV, a command-line tool, or an IoT gadget with no usable browser? Use the device flow: show the user a short code and verification URL, have them approve on their phone, and poll the token endpoint until access is granted. And if you are protecting a simple first-party API where introducing an authorization server feels like overkill, weigh that complexity honestly — for public reference data with no user accounts, no auth at all can be the pragmatic answer, as discussed in why we serve data without authentication.
Grant types to avoid: implicit and password
The implicit flow returns tokens directly in the redirect URL fragment so JavaScript can read them. That puts bearer tokens in browser history, server logs, and referrer headers, with no client authentication whatsoever. It made sense in 2012, before CORS was widely supported; today, Authorization Code + PKCE does everything it did, safely. If you inherit an app using implicit, migrate it — every major provider supports the code flow now.
The resource owner password credentials grant has the app collect the user's username and password directly and trade them for tokens. It defeats the entire purpose of OAuth2, which is that third-party apps never handle passwords, and it breaks the moment the user enables multi-factor authentication. The only defensible use is migrating a legacy first-party app that you fully control, and even there it should be a temporary bridge, not a destination. Retire both flows from any new design. For a machine-readable summary of identity mechanisms to pair with this comparison, see the authentication factors dataset.