Identify OAuth 2.0 Grant Types
Identify OAuth 2.0 grant types — OAuth 2 · OpenID Connect.
Focus: identify oauth 2.0 grant types
Staring at an OAuth 2.0 authorization server's documentation and seeing terms like authorization code, client credentials, and refresh token — and having no idea which one your app actually needs — is a recipe for security holes, broken integrations, and endless debugging. Every OAuth 2.0 grant type is a different handshake between your app, the user, and the authorization server, and picking the wrong one is like using a house key to open a bank vault: it either fails or leaves the door wide open. This lesson removes the guesswork so you can confidently identify OAuth 2.0 grant types by reading the request parameters, the client type, and the use case — and choose the right one the first time.
The problem this lesson solves
Most OAuth 2.0 failures aren't caused by bad tokens — they're caused by mismatched grant types. Your backend service tries to use the authorization code flow when it should use client credentials and gets an invalid_grant error at 3 a.m. Your single-page app tries to use the implicit flow (deprecated for security) and exposes tokens in the URL. You see a 14-field token request in the docs and have no idea which fields are required for your scenario.
Here's the pain you're escaping by learning to identify grant types:
- Guessing instead of deciding — picking a flow because a blog post used it, not because it fits your client type.
- Security theater — using the wrong flow means you either leak tokens (implicit) or store secrets where they can be stolen (password grant).
- Wasted debugging cycles — every grant type has its own required parameters, and mixing them up produces cryptic errors like
unauthorized_clientorunsupported_grant_type. - Compliance headaches — auditors ask which grant type and why; you need a defensible answer.
By the end of this lesson, you'll be able to look at any OAuth 2.0 flow — from a mobile app to a cron job — and name the grant type, its token lifecycle, and why it's (or isn't) the right choice.
Core concept / mental model
Think of OAuth 2.0 grant types as different doors into the same building (the authorization server). Each door has its own lock, and the key you hold depends on who you are:
- You're a web app with a backend — you hold a client secret and can guard it; use the authorization code door.
- You're a single-page app or mobile app — you can't keep a secret; use the authorization code + PKCE door.
- You're a server-to-server service — no user involved; use the client credentials door.
- You're a trusted first-party app that already has a user's password — use the resource owner password credentials door (sparingly, legacy).
- You're refreshing an expired token — use the refresh token door (a specialized grant).
The three core ingredients
To identify a grant type, you only need to answer three questions:
- Who is the client? — A public client (SPA, mobile) or a confidential client (backend server)?
- Is a user involved? — Does the flow need the user to authenticate and consent?
- What's the token's purpose? — Short-lived access token, long-lived refresh token, or a one-time code?
The token exchange map
Here's the high-level map of how tokens flow (we'll expand each path below):
| Grant Type | Client Type | User Involved? | Primary Output |
|---|---|---|---|
| Authorization Code | Confidential | Yes | Access + refresh token |
| Authorization Code + PKCE | Public | Yes | Access + refresh token |
| Client Credentials | Confidential | No | Access token (no refresh) |
| Resource Owner Password | Confidential (usually) | Yes (direct credentials) | Access + refresh token |
| Refresh Token | Confidential or Public | No (already authenticated) | New access token + |
Mental model tip: The grant type is not the same as the token type. The grant type is the flow that exchanges something (code, credentials, or a refresh token) for an access token. The access token is the key.
How it works step by step
Every grant type follows a similar skeleton, but the request payload and first leg differ. Let's walk through each flow logically.
1. Authorization Code Grant (confidential clients)
This is the classic flow for web apps with a backend. The user is involved, but the app never sees the user's password.
Step 1: The app redirects the user to the authorization server's /authorize endpoint with:
- response_type=code
- client_id
- redirect_uri
Step 2: The user logs in and consents. The server redirects back to redirect_uri with a one-time authorization code in the query string.
Step 3: The app's backend exchanges that code at the /token endpoint with:
- grant_type=authorization_code
- code (the one-time code)
- client_id and client_secret
- redirect_uri (must match)
Step 4: The server returns an access token and usually a refresh token.
The code is short-lived (often 5–10 minutes) and can only be used once. This prevents eavesdropping because the final exchange happens on a secure server-to-server channel.
2. Authorization Code + PKCE (public clients)
For SPAs, mobile apps, and native apps that cannot safely store a client secret, we add PKCE (Proof Key for Code Exchange). A code_verifier (a random string) and a code_challenge (hash of the verifier) are generated. The challenge goes to /authorize; the verifier goes to /token along with the code. The server verifies they match — this stops an attacker who steals the code from using it.
Step 1: Generate a code_verifier and code_challenge (e.g., S256 hash).
Step 2: Redirect to /authorize with code_challenge and code_challenge_method=S256.
Step 3: After login, get the code back.
Step 4: Exchange code for tokens at /token with code_verifier (and no client secret).
3. Client Credentials Grant (machine-to-machine)
The simplest flow — no user, no redirect. The client (typically a backend service) sends its own client_id and client_secret directly to the /token endpoint with grant_type=client_credentials. The server returns an access token that represents the client itself, not a user.
4. Resource Owner Password Credentials Grant (legacy/first-party)
Here the app collects the user's username and password and sends them directly to the /token endpoint with grant_type=password. It's only recommended for highly trusted, first-party apps (e.g., a company's own mobile app) and is now discouraged by the OAuth 2.0 Security BCP. Use it only when you can't use a redirect-based flow.
5. Refresh Token Grant
Once an access token expires, the client can send a refresh token to /token with grant_type=refresh_token, plus its client_id (and secret if confidential). The server validates the refresh token and returns a fresh access token — this keeps users logged in without re-authentication.
Hands-on walkthrough
Let's identify and implement each flow in practice. You'll use curl to hit a mock authorization server (example: https://auth.example.com). Let's start with client credentials — the easiest to get running.
Example 1: Client Credentials (server-to-server)
# Step 1: Request a token using client credentials
curl -X POST https://auth.example.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=my-service" \
-d "client_secret=super-secret"
Expected response (JSON):
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}
Note: Notice there's no
refresh_token— client credentials grants typically don't issue one. When the token expires, you simply request a new one.
Example 2: Authorization Code + PKCE (SPA)
First, generate your PKCE values (using Python for clarity):
import hashlib
import base64
import secrets
# Generate a code_verifier (43-128 chars, URL-safe)
code_verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b'=').decode()
# Compute the code_challenge (S256 method)
sha256 = hashlib.sha256(code_verifier.encode()).digest()
code_challenge = base64.urlsafe_b64encode(sha256).rstrip(b'=').decode()
print(f"code_verifier: {code_verifier}")
print(f"code_challenge: {code_challenge}")
Output (values will differ):
code_verifier: f9e6... (long random string)
code_challenge: p3bV... (28-char hash)
Now redirect the user's browser to the authorization endpoint:
https://auth.example.com/authorize?response_type=code&client_id=my-spa&redirect_uri=https://myapp.com/callback&code_challenge=YOUR_CHALLENGE&code_challenge_method=S256
After the user approves, the server redirects to redirect_uri?code=AUTH_CODE. Exchange the code:
curl -X POST https://auth.example.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=my-spa" \
-d "code=AUTH_CODE" \
-d "redirect_uri=https://myapp.com/callback" \
-d "code_verifier=YOUR_VERIFIER"
Expected response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "def50200...",
"token_type": "Bearer",
"expires_in": 3600
}
Pro tip: In a real SPA, never store the
access_tokeninlocalStorage— use in-memory or a secure HttpOnly cookie.
Example 3: Refresh Token
When the access token expires, send the refresh token:
curl -X POST https://auth.example.com/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=refresh_token" \
-d "client_id=my-spa" \
-d "refresh_token=def50200..."
Response: a new access token and optionally a rotated refresh token.
Example 4: Identify a grant type from a request log
Imagine you see this log line:
POST /oauth/token
grant_type=authorization_code
code=abc123
client_id=sample-app
client_secret=xyz
redirect_uri=https://app.example/callback
How would you identify this? Look for grant_type=authorization_code plus a client_secret — that tells you it's a confidential web app using the standard authorization code flow. If you saw code_verifier instead of client_secret, it would be PKCE.
Compare options / when to choose what
The table below summarizes the decision points. Use it as a cheat sheet.
| Grant Type | Use When | Don't Use When | Token Lifetime |
|---|---|---|---|
| Authorization Code | Web app with backend, high security | You have no backend to store a secret | Short-lived + refresh |
| Authorization Code + PKCE | SPA, mobile, native app | You have a confidential backend | Short-lived + refresh |
| Client Credentials | Server-to-server, backend cron, APIs | You need to access user data | Short-lived, no refresh |
| Resource Owner Password | First-party app, legacy, trusted | Any third-party app or if you can use redirect | Short-lived + refresh |
| Refresh Token | Extending sessions without re-login | Initial authentication only | Long-lived (rotated) |
Rule of thumb: If you can use a redirect-based flow, don't use the password grant. If you have a backend, prefer authorization code over implicit. If there's no user, use client credentials.
Variations to consider
- Implicit flow (deprecated): Used to return the token directly from the
/authorizeendpoint. It's now considered insecure because tokens appear in the URL. You should always use authorization code + PKCE instead — the modern replacement. - JWT Bearer Grant: A non-standard (but common) extension where a JWT is used as the authorization grant. Useful in enterprise federation scenarios.
- Device Authorization Grant: For smart TVs, command-line tools, or IoT devices that can't easily redirect — the user visits a URL on another device and enters a code.
Troubleshooting & edge cases
Even when you choose the right grant type, you'll hit roadblocks. Here are the common ones and their fixes:
unsupported_grant_typeerror: You sent the wronggrant_typevalue, or the server doesn't support that flow. Double-check spelling (e.g.,authorization_codevsclient_credentials).invalid_grantduring code exchange: The authorization code was already used, expired (usually 5–10 minutes), or theredirect_uridoesn't match the one used in the first step. Generate a fresh code and verify exact URI.unauthorized_client: Your client ID is not allowed to use that grant type. For example, you triedclient_credentialson a public client (SPA). Ensure your client registration allows the grant.- Missing
code_verifierin PKCE: The server rejects the exchange withinvalid_requestif you forget the verifier. Always generate and store it before redirecting. - Refresh token doesn't work after a while: Servers may rotate refresh tokens (issue a new one each time) or revoke them after a maximum lifetime. Implement token rotation and store new refresh tokens.
- Tokens in URL (implicit flow): If you see tokens in the URL, you're likely using the deprecated implicit flow. Migrate to authorization code + PKCE to keep secrets out of the browser history.
- Edge case — public client without PKCE: Some legacy APIs accept
authorization_codewithout a verifier, but this is insecure. Always send PKCE for public clients.
Debugging checklist:
- Confirm the client type (public vs confidential).
- Confirm the grant type in the request matches the one you authenticated with.
- Check that all required parameters are present (see the flow tables above).
- Check token expiry — you may simply need a refresh.
- Check client registration — is the grant type enabled for this client?
What you learned & what's next
You've learned to identify OAuth 2.0 grant types by decoding the request parameters, the client type, and the token purpose. You can now explain the core idea behind each of the five main grants — authorization code, authorization code + PKCE, client credentials, password, and refresh token — and you've completed a practical exercise using curl and Python to produce tokens.
Specifically, you achieved the lesson's learning objectives:
- Explain the core idea behind identifying OAuth 2.0 grant types — you know what each
grant_typevalue means and what triggers it. - Complete a practical exercise — you generated client credentials, executed an authorization code + PKCE exchange, and used a refresh token.
You're now ready for the next lesson in the OAuth · OpenID Connect track: handling tokens securely — where you'll learn to store, validate, and refresh tokens like a pro, and how OpenID Connect builds on OAuth 2.0 to add identity (id_token) on top of authorization.
Keep this cheat sheet close — the ability to quickly identify a grant type is the foundation for every OAuth implementation you'll ever write.
Practice recap
Run the client credentials example against a mock server, then generate PKCE values in Python and simulate a code exchange. Finally, take the server log snippet and identify the grant type — or better, write a tiny script that inspects a token request payload and prints the grant type name.
Common mistakes
- Using the implicit flow for SPAs even though it's deprecated — tokens appear in the URL and get logged in browser history. Use authorization code + PKCE instead.
- Sending a client secret with a public client (SPA/mobile) — the secret is exposed in the client bundle; PKCE is the correct companion.
- Trying client_credentials when you actually need user-specific tokens — client credentials returns a token for the app itself, not the user.
- Forgetting to include the code_verifier in the token exchange for PKCE — the server rejects with invalid_request or invalid_grant.
- Ignoring refresh token rotation — if you store the old refresh token and the server rotates it, you'll get unauthorized_client on the next refresh.
Variations
- Device Authorization Grant for devices without a browser (smart TVs, CLI tools).
- JWT Bearer Grant for exchanging a trusted JWT for a token in enterprise environments.
- Token Exchange extension (RFC 8693) to swap tokens between services.
Real-world use cases
- A Node.js backend service uses client_credentials to call a third-party REST API with its own service account.
- A React SPA uses authorization_code + PKCE to log users in via GitHub and obtain an access token for the app's API.
- A mobile banking app uses password grant for legacy first-party authentication, with refresh tokens to keep sessions alive.
Key takeaways
- The grant type is the flow that exchanges something for an access token; it's determined by client type, user involvement, and token purpose.
- Authorization code (with or without PKCE) is the most secure choice for any app with a user — use it unless you have a strong reason not to.
- Client credentials is for machine-to-machine access where no user exists, and it never issues a refresh token.
- Public clients (SPA/mobile) must use PKCE to prevent authorization code interception.
- Refresh tokens extend sessions without a new login; always rotate and store them securely.
- When in doubt, read the request parameters (
grant_type,client_secret,code_verifier) to identify which flow is in play.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.