A partner integration ships one OAuth 2.0 scope granting everything — what does that cost, and how would you shape the strings instead?
answer
- one permission, one all-or-nothing decision
- the screen nobody can read
- revoking part means revoking everything
- resource plus action per string
- narrowing later needs fresh consent
basics
~20 sOne all-powerful scope turns every consent screen into a blanket yes and makes partial revocation impossible — the owner can only keep or kill everything. Shape strings per resource and action so each names something a person can refuse.
solid answer
~50 sA single coarse scope is cheap to ship and expensive forever. The resource owner gets one all-or-nothing decision, so consent becomes nominal; an access token issued under it carries the whole account if it leaks; a lower-trust partner cannot be given a smaller grant because no smaller grant exists; and there is no incremental path, because the first ask is already the maximum. Worst, revocation is symmetric with granting: if there is only one string, withdrawing part of the access is not expressible. The fix is a namespace shaped around what a person recognises — a string per resource and action, such as `hives:read`, `hive-notes:read` and `treatments:write`, each with one plain phrase on the consent screen, read split from write. A string per API endpoint is the opposite failure: forty lines nobody reads buys no real choice either.
code
json · 6 lines{
"hives:read": "See your hives and where they are kept",
"hive-notes:read": "Read health notes recorded on your hives",
"treatments:write": "Record a treatment on one of your hives",
"members:read": "See the association's member list"
}go deeper
Know that a scope string is what the resource owner is agreeing to, so one string meaning everything leaves them a single all-or-nothing decision to make.
Explain the split you would make — one string per resource and action, phrased so a person reading the screen can tell what they are approving and what they are refusing.
Show the consequences you have operated: a leaked token's blast radius, partner tiering, and the fact that narrowing an existing coarse string means re-consenting every grant that already exists.
Own the vocabulary across the estate — who may mint a new scope string, how many a consent screen carries before it stops being read, and how the set is versioned and retired.
## Why the god scope keeps getting shipped It is genuinely easier. One string means no mapping table, no per-feature checks at the boundary, no decisions about where a line falls, and a client integration that never has to handle a partial grant. Every one of those savings is real on the first day and is paid back with interest by the people who operate the thing afterwards. ## What it actually costs - **Consent becomes nominal.** A screen saying *this application will have full access to your account* offers no decision worth making; the person either abandons the integration or approves something they could not evaluate. - **The blast radius is the whole account.** An access token is honoured on presentation, so whatever the grant covers is what a leaked token covers. A grant that covers everything makes every leak maximal. - **Partners cannot be tiered.** A read-only analytics integration and a full write integration are the same request, so a lower-trust partner has to be given the same power as a trusted one. - **There is no incremental path.** The first ask is already the maximum, so nothing can be deferred to the moment it makes sense. - **Support cannot explain anything.** When a call fails there is no vocabulary for *what this grant covers*, because it covers everything or nothing. ## The revocation asymmetry This is the point worth saying out loud in an interview. The authorization server's ability to grant **less** than was requested operates per scope string: it may fully or partially ignore the request, and it reports the subset that survived. With exactly one string, *partial* is not expressible. The resource owner's only lever is the whole grant — keep it or destroy it — and destroying it takes down every feature the integration provides, including the ones they were happy with. Fine-grained consent and fine-grained revocation are the same design decision seen from two ends. ## Shaping a namespace | shape | example | what happens | |---|---|---| | one string for everything | `hive-register` | consent is a blanket yes; no partial revocation | | one string per API endpoint | `get-hive`, `get-hive-notes`, `post-treatment`, ... | a screen nobody reads; the choice is theoretical | | one per resource and action | `hives:read`, `hive-notes:read`, `treatments:write` | each line is refusable and means something | Practical rules for the middle column: 1. **Name the resource a person recognises**, not the internal table or service. 2. **Split read from write**, because that is the split people actually care about. 3. **Write one plain phrase per string** and check the whole screen reads in a few seconds. 4. **Keep the fine-grained checks inside the API**, where per-record rules belong; a scope is the coarse boundary the owner agreed to, not an access-control language. ## Narrowing an existing coarse scope You cannot quietly redefine a string that grants already reference. Every existing grant keeps whatever the string now means, so narrowing it breaks working integrations with no signal, and widening it hands out access nobody approved. The workable sequence is additive: 1. define the new strings alongside the old one; 2. accept both for a period, and report the granted set honestly in every token response; 3. move clients to request the narrow strings, which means their users re-consent; 4. retire the coarse string once nothing requests it. That is a migration with a human step in the middle — every affected resource owner sees a consent screen again — which is precisely why the shape is worth getting right before the first partner integrates. ## What an interviewer is listening for Not 'least privilege' as a slogan. The observable answers are: the owner's decision must be refusable in parts; the number of strings is bounded by what a screen can carry; and a coarse string, once granted, can only be replaced, never quietly redefined.
- Why can you not simply redefine the coarse scope to mean less?Because existing grants reference that string, and every client holding one keeps whatever it now means. Narrowing it breaks working integrations with no signal; widening it hands out access nobody approved. The safe path is new strings alongside the old one, clients moved across, and the coarse string retired once nothing requests it.
- How many scope strings is too many?The consent screen sets the limit: if the resource owner cannot read the list and decide, the extra granularity has bought a longer screen and no real choice. A string per endpoint usually fails that test. Group by the resource a person recognises and the action taken on it, and keep per-record rules inside the API.
- Does a narrower scope help if an access token leaks?Yes, bounded by exactly what that grant carries. A token issued for `hives:read` cannot record a treatment, so the loss is limited to what was granted rather than to what the client could have asked for. That is the concrete argument for not requesting write access until a feature genuinely needs it.
saying these in an interview costs you the question
- Says one coarse scope is fine because the API checks permissions anyway
- Defines one scope per endpoint and calls that fine-grained
- Thinks a coarse scope can be narrowed without re-consent
- Names scope strings after internal services nobody outside recognises
- Treats the consent screen as legal text rather than a decision