How OAuth 2 Scope Varies Across Providers
With the beta release of OAuth for the Heroku Platform API, Heroku joins a crowded field of providers implementing scoped access tokens. The OAuth 2 specification (RFC 6749) intentionally leaves the format of scope strings open-ended, and the result is a wide variety of approaches across major platforms. Understanding those differences informed Heroku's own design decisions.
What the Spec Requires
RFC 6749 establishes the basic mechanics for scope without prescribing naming conventions:
- Clients request scope via the
scopeparameter on the authorization or token endpoint. - Scope strings are case-sensitive and space-delimited.
- If the authorization server modifies the requested scope, it must include the actual
scopein its response. - When no scope is specified, the server either falls back to a documented default or fails the request.
Everything else, including the format of individual scope strings, is left to each provider.
Survey of Existing Implementations
Providers have found divergent solutions for representing permissions:
- App.net uses a minimal set of snake_case strings with no namespace hierarchy.
- Facebook suggests comma-delimited strings rather than spaces, uses dotted paths for hierarchy (e.g.
user_actions.video), and supports dynamic segments whereAPP_NAMESPACEselects the target app. - GitHub offers a concise set where a colon creates a parent-child relationship (e.g.
user:emailis narrower thanuser). GitHub also returnsX-OAuth-ScopesandX-Accepted-OAuth-Scopesheaders on API responses to make required and granted scopes self-documenting. - Google begins every scope string with either
openid,email, orprofile, then extends to absolute URIs for more granular access across its ecosystem. - Instagram departs from the spec's space delimiter and joins its simple scope list with plus signs.
- LinkedIn uses underscores to pair a resource type with its permission mode, where
ris read andwis write. - Salesforce requires a dedicated
refresh_tokenscope string to receive a refresh token. - Shopify mixes read/write distinctions into the string itself and implicitly grants
read_privileges on any resource scoped withwrite_. - Windows Live ID prefixes every string with
wl.so scopes remain unique across all of Microsoft's properties.
Heroku's Scope Design
Heroku's implementation was shaped by several engineering goals. Scope strings should stay minimal and general enough to map to future systems, because extending a set of strings is easy while deprecating already-public ones is disruptive. Some resources, such as an app's config vars, are sensitive even within an app-wide permission—those need protection at a separate level. And there is a need for a bare-minimum scope that grants access to nothing but basic user identity, useful to apps that use OAuth mostly for authentication.
The resulting six scope strings form a strict hierarchy:
identity: Access to the user's account info via a single endpoint, and nothing else.read: Read-only access to a user's apps and non-sensitive subresources.write: Write access to apps plus non-sensitive subresources; impliesread.read-protected: Read access including protected subresources like config vars and releases.write-protected: Write access including protected subresources; implieswriteandread-protected.global: Unrestricted access, a superset of all other strings.
On the backend, these public strings map to far more granular permission rules, leaving room for the public scope set to grow as requirements change. Heroku also follows GitHub's lead in making scope requirements self-documenting; its API endpoints return the accepted scope strings as standard response headers rather than the deprecated X- prefixed versions.
The scope design is intentionally not final. Heroku expects to continue iterating as internal and external consumers surface new requirements. The goal is to preserve the flexibility and precision promised by the spec while maintaining a simple, readable interface consistent with how the broader web has settled on OAuth scoping.



