Scope Best Practices
On this page
What are OAuth 2.0 Scopes?
OAuth 2.0 scopes are strings issued to access tokens. The Introduction to Scopes explains how APIs use scopes to restrict access to resources. A common way to get started with scopes is to use a combination of the type of resource and the access required on it:
Resource Type | Access Level | Scope Value |
---|---|---|
order | read | order_read |
OAuth standards documents do not define how you should use scopes but leaves that to designers of each system. This article explains how to manage scopes at scale, avoid common problems, and use some advanced scope techniques. For that, it uses an example to showcase the arguments.
Example Business Scenario: APIs and Clients
When organizations provide digital services they usually develop multiple APIs and clients that span a number of business areas. For example, a system that sells products to online customers might use various components like those shown here, targeted at different roles of user.
You need to ensure that all components in your system can send or receive scopes in access tokens so that you correctly restrict access to resources.
Scopes in Clients
You need to configure each client with scopes to restrict its API privileges. When a client receives an access token, the scopes in the token represent the current API privileges of the client. When a client serves users, those users must authenticate. The authorization server then returns an access token to the client containing the user's identity.
Once user authentication completes, the authorization server can present a consent screen to the user before it issues the access token. You should activate user consent at the authorization server when a third-party client requests access to APIs. The user may grant the third-party access to some scopes and not others. Therefore, in the general case, the scopes granted may be a subset of those requested by the client.
After user authentication and consent, the authorization server issues the granted scopes to the access token. The grant usually lasts for an entire authenticated user session, during which the client silently refreshes access tokens. Token refresh can either use all scopes granted or a subset of them.
First Party APIs and User Consent
It is common to use OAuth for first-party use cases, where an organization provides both APIs and clients and owns all of the data. In such cases, consider disabling user consent, to avoid user confusion.
Scopes Design
When you design scopes for real-world systems you set boundaries on where clients can use access tokens. To enable this, APIs should separate data both by business area and data sensitivity, so that you expose parts of your data to different clients. You should control which clients can gain access to which API privileges.
Use Hierarchical Scopes
When data is hierarchical you can use hierarchical scopes, as in the below examples, where colon characters represent subresources:
Scope | Grants access to |
---|---|
order | Full information about orders. |
order:item | Information about items within an order. |
order:payment | Access to order payment details. |
order:shipping:address | Information about where to deliver orders. |
order:shipping:status | Information about the delivery status of orders. |
Use Least-Privilege Scopes
Design clients so that they only have access to the data they need, and limit the scope to read-only access when write access is not needed. One possible convention is to make read-only access the default and then add a write
suffix when higher privilege is needed:
Scope | Access Granted |
---|---|
order | Read only access to full order information. |
order:items | Read only access to details about order items. |
inventory:write | The ability to create, change or delete an entire inventory item. |
inventory:price:write | The ability to change the price details for an inventory item. |
Use Stable Scopes
It is possible to use many fine-grained scopes. Different API endpoints can each require their own scopes. Usually though, it is better to keep scopes simple when you get started, then add more scopes later when you identify the need.
For example, you may have three business areas of marketing
, insurance
and finance
and use scopes to represent each of them. These high-level scopes are stable and sufficient to set boundaries on where clients can use access tokens.
Avoid Scope Explosion
Without care, there is a risk that you could create a large number of scopes that are difficult to maintain over time, leading to scope explosion. One cause of scope explosion is when client-specific concerns leak into scope names, as in the following examples, resulting in duplication:
- inventory:supplier:2
- order:admin-usa:write
Configuring excessive scopes in the authorization server can have an adverse impact on your productivity. Instead, you should only need to add new scopes occasionally, such as when you expand APIs to cover a new business area:
- Avoid frequently upgrading clients to use new scopes.
- Avoid the need for access token versioning in your APIs.
- Avoid deploying frequent scope configuration updates to your authorization server.
Example Business Scopes
A moderately complex client might interact with a number of APIs and use multiple scopes such as those listed in the following table. The teams working in a business area should be able to understand the scopes you design.
Scope | API Access Granted |
---|---|
openid | An OpenID Connect scope that represents the user's identity. |
profile | An OpenID Connect scope that represents the user's name-related details. |
customer:benefits | Access to customer benefits. |
inventory | Access to inventory details. |
orders:write | Access to create orders and then view or update them afterwards. |
shipping:write | Access to change delivery information. |
API Authorization Using Scopes
APIs verify access token scopes to perform high-level authorization checks. An API endpoint that requires an insurance
scope would immediately deny access if that scope is not present in an access token for a user, even if that user has insurance rights in another application.
Verifying scopes is only an entry-level check and not a complete API authorization solution. In particular, scopes are usually always fixed at design time. This means, you cannot use scopes for dynamic authorization, i.e. you cannot use different scopes for distinct users of your frontend applications.
Always Enforce Scopes
Your API authorization must always verify that required scopes are present and return a 403 forbidden response to the client when scope verification fails. In the following example, the hasScope
method might return true if there is an exact match on the order:item
scope, or if the parent scope of order
is present:
public getOrderItems(criteria: Criteria): OrderItem[] {if (!claimsPrincipal.hasScope("order:item"))throw forbiddenError();}return repository.getOrderItems();}
You could design your APIs to require an additional suffix for data changing commands, in which case the API authorization code for that type of operation could check for a precise scope:
public void updateInventoryPrice(item: OrderItem) {if (!claimsPrincipal.hasScope("inventory:price.write"))throw forbiddenError();}return repository.createInventoryItem(item);}
You can optionally use a reverse proxy or API gateway to enforce scopes. Doing so prevents obviously invalid calls from ever reaching the actual API, which reduces costs in some API architectures.
Use Scopes for Coarse-Grained Access Control
In the example business scenario you might enforce these high-level user authorization rules with scopes:
User Role | Scopes | Authorization Rule |
---|---|---|
Customer | customer:benefits | A customer can view but not change their benefits. |
Administrator | inventory:write | An administrator can create or update inventory items and their prices. |
Supplier User | inventory:sales | A supplier business partner is able to view reports on sales of their inventory items. |
Use Claims for Fine-Grained Access Control
Scopes only enable entry-point API authorization. Although it often makes sense to consider read and write permissions, this approach has some limitations. For example, an API may need to allow read-write access to some resource properties and read-only access to others. Your main API authorization may also need to enforce dynamic rules such as those listed below. These rules need to use dynamic behavior based on properties of the authenticated user.
User Role | Authorization Rule |
---|---|
Customer | A customer can only view benefits and orders associated to their own user ID. |
Customer | A customer with a higher subscription levels can access additional inventory. |
Administrator | An administrator may have access to all data, but with regional restrictions. |
Supplier User | A supplier business partner can only view inventory for their own company's supplier ID. |
Therefore, you should handle the finer details of authorization using Claims, another part of the security architecture. When you issue scopes to access tokens, you also issue a set of claims. The claims enable your detailed API authorization.
High-Privilege Scopes
A client can use scopes to temporarily get a high-privilege access token to call particular API endpoints. For example, a client might have access to an order:payment scope but not use it by default. When the user needs to make a payment, the client could issue an authorization redirect to the authorization server and request the high-privilege scope. The flow might include user consent. The client then receives an access token with which it can call a high-privilege API endpoint.
Prefix Scopes
Although scopes are not usually dynamic, prefix scopes are an exception to that rule. Prefix scopes enable clients to request scopes that are unknown at design time. To use a prefix scope you configure a scope name like transaction-
in the authorization server. The convention is to use a trailing hyphen in the name. The client then asks for a scope containing a specific ID at runtime. The scope granted is always for a concrete resource and you can visualize the full scope in a consent screen:
Prefix scopes can be useful in advanced scenarios, such as Financial-grade use cases. More commonly, scopes should not usually identify concrete resources. Another limitation of prefix scopes is that they cannot contain claims.
Scopes and Time to Live
You usually configure clients to use access tokens with a short expiry time, such as 15 minutes. Clients then silently refresh access tokens within the same authenticated user session. By default, the authorization server uses the scopes that a user granted for its refresh token logic that issues new access tokens. This may not be the desired behavior for high-privilege scopes.
The authorization server should enable you to configure a time-to-live for scopes. When you make an authorization request with such a scope, the scope is only issued to access tokens for the configured time. When you refresh an access token after the configured time, the new access token no longer contains the scope.
Scopes and Multiple APIs
When you develop multiple APIs and they need to call each other, you can forward the access token from the client to upstream APIs. This approach is common in a small microservices setup, where each API checks for its required scopes, and is the simplest way to use scopes across multiple APIs:
Yet when you use multiple APIs, you should avoid designing scopes in an overly technical manner. For example, if you split a large API into three smaller microservices, for code manageability reasons, you should not need to add any new scopes.
Use Token Exchange to Change Scopes
Forwarding access tokens may not always be the most secure option. More generally, when APIs call each other you choose a token sharing approach. The source API can use OAuth token exchange to get a new access token with different scopes to send to the target API. Most commonly, you downscope
access tokens. For example, you might do so when you call APIs operated by a subdivision of your organization.
The following flow shows the use of token exchange to downscope an access token when an orders API calls a shipping API. The only scopes left in the access token are those that the shipping API needs. The new access token's user identity, expiry and other claims can remain the same as those in the original access token.
The forwarding API sends a token exchange request to the token endpoint of the authorization server. To use token exchange you must register the forwarding API as a client of the authorization server. The following example shows how an API can make a token exchange request:
curl -X POST https://localhost:8443/oauth/v2/oauth-token \-H 'Content-Type: application/x-www-form-urlencoded' \-d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \-d 'client_id=orders-api' \-d 'client_secret=EFfA7Ml9kkIqJPcFNuo-z' \-d 'scope=shipping.write' \-d 'token=56acc3f6-b9ef-4a34-a9d4-f9d7a27a505b'
You can also potentially "upscope" access tokens, though you should avoid doing so when the user consents to the scopes issued to access tokens. You can read more about token exchange variations in the token exchange flow article.
Scope Best Practices Summary
Scopes represent an area of data and allowed operations on that data. Use the following guidelines when you design scopes to secure your APIs:
- Always use scopes in APIs and enforce them at every API endpoint.
- APIs must return a 403 forbidden response when an access token has insufficient scope.
- Enable user consent when a user needs to grant scope-based access to a third-party client.
- Consider using API business areas as scopes.
- Consider adding new scopes only when you add new API business areas.
- Keep scopes stable to avoid scope explosion.
- Use scopes to enable high-privilege access tokens.
- Design scopes so that they work for end-to-end flows that use multiple APIs.
- Use token exchange to downgrade scopes when calling less trusted upstream APIs.
Conclusion
Scopes provide high-level access control for your APIs. You should be able to scale your use of scopes to many APIs with good manageability. Scopes do not provide a full API authorization solution. To complete your API authorization you also need to follow Claims Best Practices.
Gary Archer
Product Marketing Engineer at Curity
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial