An overview of the best practices for designing OAuth scopes in real world systems and managing them at scale.

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 TypeAccess LevelScope Value
orderreadorder_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.

Example Business Scenario: API Scopes and Clients

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.

Scopes and user consent example

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.

Illustration of APIs separating data both by business area and data sensitivity, so that different parts of the model can be exposed to different clients

Use Hierarchical Scopes

When data is hierarchical you can use hierarchical scopes, as in the below examples, where colon characters represent subresources:

ScopeGrants access to
orderFull information about orders.
order:itemInformation about items within an order.
order:paymentAccess to order payment details.
order:shipping:addressInformation about where to deliver orders.
order:shipping:statusInformation 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:

ScopeAccess Granted
orderRead only access to full order information.
order:itemsRead only access to details about order items.
inventory:writeThe ability to create, change or delete an entire inventory item.
inventory:price:writeThe 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.

ScopeAPI Access Granted
openidAn OpenID Connect scope that represents the user's identity.
profileAn OpenID Connect scope that represents the user's name-related details.
customer:benefitsAccess to customer benefits.
inventoryAccess to inventory details.
orders:writeAccess to create orders and then view or update them afterwards.
shipping:writeAccess 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:

typescript
12345678
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:

typescript
12345678
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.

The entry point to an API, a Reverse Proxy or API Gateway, can enforce high-level scopes for particular API operations.

Use Scopes for Coarse-Grained Access Control

In the example business scenario you might enforce these high-level user authorization rules with scopes:

User RoleScopesAuthorization Rule
Customercustomer:benefitsA customer can view but not change their benefits.
Administratorinventory:writeAn administrator can create or update inventory items and their prices.
Supplier Userinventory:salesA 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 RoleAuthorization Rule
CustomerA customer can only view benefits and orders associated to their own user ID.
CustomerA customer with a higher subscription levels can access additional inventory.
AdministratorAn administrator may have access to all data, but with regional restrictions.
Supplier UserA 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:

consent to prefix scopes

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.

Scope expiry

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:

The token issued to the client being forwarded to other 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.

Downgrading scopes via token exchange

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:

bash
1234567
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.

Photo of Gary Archer

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