The Core Question: Who Is the Actor?
Every grant selection decision reduces to one question: is there a human resource owner delegating access, or is the client acting purely as itself? If a specific user needs to grant an application access to their own data, that's Authorization Code with PKCE, full stop, regardless of whether the client is a web app, SPA, or mobile app. If there's no user at all, a backend job talking to another backend service, that's Client Credentials. Almost every OAuth misconfiguration traces back to conflating these two cases: using Client Credentials where per-user delegation was actually needed, or building a custom non-standard flow because a team wasn't sure which standard grant fit.
Cricket analogy: It's like deciding whether a substitute fielder needs the captain's specific on-field authorization for that match (a delegated, person-specific grant) versus a groundstaff member who just needs their own staff badge to enter the facility, no captain involved at all.
A Practical Decision Framework
In practice: web apps with a server-side back end and browser-based SPAs and mobile apps should all use Authorization Code with PKCE for anything involving a logged-in user, the difference is only in where the code exchange happens (server-side directly, or via a BFF for SPAs). Backend services calling other backend or third-party APIs with no user context use Client Credentials. Devices with limited or no input capability, smart TVs, CLI tools without a browser, use the Device Authorization Grant (not covered in depth here, but worth knowing it exists specifically for that case). Anything reaching for the Implicit or Password grants should stop and reconsider, per the deprecated-grants discussion, there is essentially always a better modern option.
Cricket analogy: It's like a team having a clear playbook: a specific batter walking to the crease follows the standard umpire sign-in procedure (Authorization Code), while the groundskeeper follows a totally different facility-access procedure (Client Credentials), and nobody improvises a third, undocumented way in.
Scoping and Least Privilege Across Grants
Whichever grant you choose, scope design should follow least privilege: request only the specific permissions the current operation needs, not a broad 'everything' scope out of convenience. A read-only reporting dashboard should request orders:read, not orders:write or admin:*; a Client Credentials service that only reorders inventory should never also carry a scope that lets it issue refunds. This matters doubly for Authorization Code flows, where overly broad scope requests also erode user trust at the consent screen, users are more likely to abandon a login flow that asks for access it clearly doesn't need for its stated purpose.
Cricket analogy: It's like giving a substitute fielder specific permission to field at deep cover for one over, not a blanket pass to bowl, bat, and make tactical decisions for the rest of the match; the access should match the exact job at hand.
Decision checklist:
1. Is a specific human user delegating access to their own data/actions?
-> YES: Authorization Code + PKCE
- Web app with server back end: exchange code server-side
- SPA: exchange code via CORS-enabled /token, or better, a BFF
- Native/mobile app: exchange code in-app, PKCE mandatory
-> NO, continue to 2.
2. Is this a backend/service calling another API with no user context?
-> YES: Client Credentials
3. Is this a device with no/limited browser or keyboard input
(smart TV, CLI without local browser, IoT)?
-> YES: Device Authorization Grant
4. Are you about to reach for Implicit or Password (ROPC)?
-> STOP. Re-evaluate against options 1-3; a fitting modern
grant almost always exists.A related but distinct pattern worth knowing: when a backend service needs to call a downstream API on behalf of a user who already authenticated to it (not itself), the right tool is typically token exchange (RFC 8693) or an 'on-behalf-of' flow, not Client Credentials with a bolted-on user_id parameter. This preserves the original user's identity and scope constraints through the whole call chain.
Red Flags in a Grant Selection Review
When reviewing an OAuth integration, several signals reliably indicate a wrong grant choice: a Client Credentials token being used with a caller-supplied user or account ID parameter (should be Authorization Code or token exchange); a mobile or SPA app embedding a client_secret in its binary or bundle (the app is public, so it needs PKCE, not a secret it can't actually protect); a backend prompting users to type their password into a custom form that then calls another provider's API (ROPC in disguise, likely violating that provider's terms of service); and any homegrown 'bearer token' scheme invented because a team found OAuth's standard grants 'too complicated' for their use case, which usually means missing expiration, rotation, or scope enforcement entirely.
Cricket analogy: It's like a review flagging a groundstaff pass being used to admit a specific spectator by name, a clear sign the wrong kind of pass is being used for the wrong job, and the spectator should have gone through the normal ticketing gate instead.
If a design doc says 'we'll just use Client Credentials and pass the user ID as a parameter', that is almost always a red flag for an authorization bypass waiting to happen. The fix is nearly always Authorization Code with PKCE (if a user is present and interactive) or token exchange/on-behalf-of (if a backend needs to relay an already-authenticated user's identity downstream).
- The first question in grant selection is always whether a specific human resource owner is delegating access, or whether the client is acting purely as itself.
- Authorization Code with PKCE covers essentially every user-facing login scenario across web, SPA, and mobile clients.
- Client Credentials is for backend-to-backend calls with no user context, never for fetching per-user data via an ID parameter.
- Device Authorization Grant exists specifically for input-constrained devices like smart TVs and CLI tools.
- Implicit and Password (ROPC) grants should be treated as deprecated red flags, not viable options, in any new design.
- Least-privilege scoping applies to every grant: request only the exact permissions the current operation needs.
- Token exchange (RFC 8693) or on-behalf-of flows, not Client Credentials with a bolted-on user ID, is the correct pattern for relaying user identity through a service chain.
Practice what you learned
1. What is the first question to ask when choosing an OAuth 2.0 grant type?
2. A CLI tool running on a headless server with no browser needs to let a user link their account. Which grant is designed for this case?
3. Why is a Client Credentials token combined with a caller-supplied user_id parameter considered a red flag?
4. What is the correct pattern for a backend service that needs to relay an already-authenticated user's identity to a downstream API?
5. Why should least-privilege scoping matter even more when reviewing a grant selection?
Was this page helpful?
You May Also Like
Authorization Code with PKCE
The Authorization Code grant is OAuth 2.0's most secure and widely used flow, and PKCE hardens it further so it is safe for public clients like single-page apps and mobile apps.
The Client Credentials Grant
The Client Credentials grant lets a service authenticate as itself, without any user in the loop, making it the standard choice for machine-to-machine and backend-to-backend API access.
The Refresh Token Grant
Refresh tokens let a client obtain new access tokens without repeating the full authorization flow, keeping short-lived access tokens practical for long-running sessions.
Deprecated Implicit and Password Grants
The Implicit and Resource Owner Password Credentials grants were early OAuth 2.0 patterns now formally deprecated in OAuth 2.1 due to fundamental security weaknesses, and understanding why matters for spotting legacy risk.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics