Identify OAuth 2.0 Grant Types

Identify OAuth 2.0 grant types — OAuth 2 · OpenID Connect.

Focus: identify oauth 2.0 grant types

Sponsored

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_client or unsupported_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:

  1. Who is the client? — A public client (SPA, mobile) or a confidential client (backend server)?
  2. Is a user involved? — Does the flow need the user to authenticate and consent?
  3. 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_token in localStorage — 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 /authorize endpoint. 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_type error: You sent the wrong grant_type value, or the server doesn't support that flow. Double-check spelling (e.g., authorization_code vs client_credentials).
  • invalid_grant during code exchange: The authorization code was already used, expired (usually 5–10 minutes), or the redirect_uri doesn'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 tried client_credentials on a public client (SPA). Ensure your client registration allows the grant.
  • Missing code_verifier in PKCE: The server rejects the exchange with invalid_request if 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_code without a verifier, but this is insecure. Always send PKCE for public clients.

Debugging checklist:

  1. Confirm the client type (public vs confidential).
  2. Confirm the grant type in the request matches the one you authenticated with.
  3. Check that all required parameters are present (see the flow tables above).
  4. Check token expiry — you may simply need a refresh.
  5. 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_type value 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

  1. Device Authorization Grant for devices without a browser (smart TVs, CLI tools).
  2. JWT Bearer Grant for exchanging a trusted JWT for a token in enterprise environments.
  3. 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.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.