Originally published at GeekCoding101.
Below is a table summarizing the different grant types in OAuth 2.0 along with brief descriptions and recommendations regarding their use:
| Grant Type | Description | Recommendation |
|---|---|---|
| Authorization Code | The most commonly used flow in OAuth 2.0. It involves the exchange of an authorization code for an access token. Suitable for server-side web applications and confidential clients. | Recommended for web applications and confidential clients. |
| Implicit | Designed for user-agent-based clients (e.g., browser-based JavaScript applications). Access token is returned directly to the client without an authorization code exchange. | Deprecated due to security concerns. |
| Resource Owner Password Credentials | Allows the client to exchange the user's username and password for an access token directly. Generally discouraged due to security implications and lack of federation support. | Not recommended unless unavoidable legacy scenarios. |
| Client Credentials | Enables clients to directly exchange client credentials (client ID and client secret) for an access token. Typically used for machine-to-machine communication. | Recommended for machine-to-machine communication. |
| Refresh Token | Allows clients to request a new access token without requiring the user to re-authenticate. It's not a grant type but rather a mechanism for obtaining new access tokens. | Recommended for long-lived sessions and offline access. |
It's important to note that while some grant types may be deprecated or discouraged due to security concerns or lack of use cases, their applicability can vary based on specific requirements and use cases. However, it's generally recommended to adhere to best practices and use the authorization code flow whenever possible for enhanced security and flexibility.
How Each Grant Type Works
The table tells you which grant to pick. This section shows what each one actually does on the wire. Parameter names follow RFC 6749 and, for the device flow, RFC 8628.
Authorization Code with PKCE
Use it when a user signs in and your app acts on their behalf: server-rendered web apps, single-page apps and native or mobile apps. The client sends the user to the authorization server, the user logs in and consents, and the server redirects back with a short-lived code. The client then exchanges that code, over a direct back-channel request, for tokens.
With PKCE, the client first creates a random code_verifier and sends its hash as code_challenge in the authorization request:
GET /authorize?response_type=code
&client_id=my-app
&redirect_uri=https://app.example.com/callback
&scope=openid%20profile
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
When it redeems the code, the client sends the original verifier in the token request:
POST /token
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://app.example.com/callback
&client_id=my-app
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
An attacker who steals the code from the redirect cannot redeem it, because they do not have the verifier. OAuth 2.1 and current security guidance make PKCE mandatory for public clients and recommend it for confidential ones too. For the full step-by-step flow, see OAuth 2.0 Authorization Code Flow, Step by Step.
Client Credentials
Use it for machine-to-machine calls where there is no user: a backend service, a cron job or a CI pipeline calling an API as itself. The client authenticates with its own credentials and gets an access token directly, with no browser and no redirect:
POST /token
Authorization: Basic base64(client_id:client_secret)
grant_type=client_credentials&scope=reports.read
Because there is no user, the token represents the application, and the scopes you grant should be as narrow as possible. Only confidential clients that can keep a secret should use it. Never ship a client secret inside a browser or mobile app.
Device Authorization (Device Code)
Use it for devices that have no browser or an awkward keyboard: smart TVs, game consoles, CLI tools, IoT. The device asks the authorization server for a device code, shows the user a short user_code and a URL, and the user completes the login on their phone or laptop. Meanwhile the device polls the token endpoint:
POST /device_authorization
client_id=my-tv-app&scope=profile
-> { "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://example.com/device",
"interval": 5 }
POST /token
grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
&client_id=my-tv-app
While the user has not finished, the server answers authorization_pending (keep polling at the given interval, and back off on slow_down); once they approve, the response contains the tokens. Device codes are short-lived and the user code should be hard to guess, since anyone who types it in can approve the request.
Refresh Token
A refresh token lets a client get a new access token without sending the user through login again. Access tokens are kept short-lived (minutes to an hour) so a leak is limited; the longer-lived refresh token stays on the server or in secure storage and is exchanged at the token endpoint:
POST /token
grant_type=refresh_token
&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
&client_id=my-app
The server returns a new access token and often a new refresh token. For public clients, current guidance is to use refresh token rotation: each refresh token works once, and if an old one is replayed the server revokes the whole token family. Request the offline_access scope (or whatever your provider uses) if the app needs to keep working after the user has left.
Implicit and Password: Why They Are No Longer Recommended
- Implicit returned the access token directly in the URL fragment of the redirect. Tokens ended up in browser history, referrer headers and logs, and there was no way to bind them to the client that asked. The OAuth 2.0 Security Best Current Practice (RFC 9700) advises against it, and OAuth 2.1 removes it. Single-page apps should use authorization code with PKCE instead.
- Resource Owner Password Credentials has the app collect the user's username and password and send them to the token endpoint. This teaches users to type their password into third-party apps, breaks multi-factor authentication and single sign-on, and gives the client far more power than it needs. OAuth 2.1 removes it as well. Migrate to authorization code with PKCE, or to the device flow when there is no browser.
Which Grant Type Should I Use?
| Your app | Use |
|---|---|
| Web app with a server backend, user signs in | Authorization Code (with PKCE) |
| Single-page app or mobile/native app | Authorization Code with PKCE |
| Backend service or script, no user | Client Credentials |
| TV, console, CLI or IoT device | Device Code |
| Keeping a session alive without re-login | Refresh Token alongside one of the above |
| Old app still on Implicit or Password | Plan a migration to the rows above |
Is PKCE A Grant Type?
No, PKCE (Proof Key for Code Exchange) is not a grant type in OAuth 2.0.
Instead, PKCE is an extension to the OAuth 2.0 authorization code flow, designed to enhance security, particularly in scenarios where the client secret cannot be reliably stored, such as mobile or native applications.
In the OAuth 2.0 authorization code flow with PKCE, the core grant type remains the "Authorization Code" grant type.
PKCE introduces additional security measures during the authorization code exchange process by utilizing a dynamically generated secret (the code verifier) and a hash-based challenge (the code challenge). This mechanism helps mitigate certain security risks, such as authorization code interception attacks.
Top comments (1)
the table plus the on-the-wire section is a nice combo, most explainers stop at the table. one thing worth adding: current guidance (OAuth 2.1 draft) says use PKCE for every authorization code flow, confidential server-side clients included, not only SPAs and mobile. it costs almost nothing and blocks code-interception even if the client secret leaks. also for SPAs i'd still keep tokens out of localStorage and use a small backend that holds them in an httpOnly cookie