Using scopes vs. permissions for application authorization
The post is a homage to the legendary Vittorio Bertocci, who wrote a great article about the subject a couple of years ago.
One of the earliest authorization patterns organizations fall into is using scopes embedded in access tokens to represent permissions. While this might be a popular first step for organizations, it shouldn’t be the sole mechanism for determining access to your application, for a number of reasons.
In this post, we dive into those reasons and explain what scopes are and why they shouldn’t be used as application permissions.
The basics
Before we specify the reasons you should not use OAuth 2.0 scopes as application permissions or user privileges, let’s first define these concepts.
What are permissions?
A permission describes the ability to perform an action on a resource. For example, a document can be viewed, edited, and deleted.
Permissions can be assigned to users. When you assign a permission to a user, you are granting that user that privilege for a specific resource, or set of resources.
In a file sharing application, for example, documents might have can-view
, can-edit
, and can-delete
permissions. A user that is assigned can-view
permissions will be able to view documents, and a user that is assigned can-edit
permissions will be able edit those documents.
What are OAuth 2.0 scopes?
Scopes were introduced in OAuth 2.0 to allow an application to request a set of capabilities from a resource server on behalf of the user. Scopes typically define broad categories of access - for example, read your emails or update your calendar. They also allow a user to constrain what an application can do on their behalf.
The reason that scopes are often used for authorization is because the very first authorization pattern developers implement lends itself well to scopes. This pattern tends to involve differentiating “admins” who should have all the privileges from “non-admins” who should not. It’s very easy to create a scope to represent the “admin” privileges. When an admin user logs in the identity provider places this scope into the JSON Web Token (JWT) that is minted for that user, and the “authorization logic” simply checks that the JWT contains that scope. Simple, right?
When you only have admins and non-admins everything works fine. But once the application is required to implement more granular permissions, scopes are no longer a viable solution for a few reasons, including size limits, staleness of information, lack of granularity, and more.
Scopes vs permissions
Scopes were never intended to be the basis of application authorization systems. They were meant to limit the access an application has to a user’s account. They were developed for delegated access scenarios, not to function as application permissions. They are used for authorization due to the ease with which identity providers and authentication services allow developers to add them to access tokens. However, they have several significant limitations.
When comparing scopes vs permissions as the basis of an authorization system, scopes have the following shortcomings:
1. Size limits
As applications grow in size and complexity, the number and types of resources they have also tends to increase. A document sharing app will have text documents, spreadsheets, images, folders, organizations, etc. Each resource type will likely support different operations (e.g. images aren’t editable, while text documents and spreadsheets are). Any fine-grained authorization system will create a matrix of the cross product of types and permissions, resulting in dozens if not hundreds of scopes. Injecting all of these into a JWT isn’t possible, due to the size limit of the HTTP authorization header.
2. Lack of granularity
OAuth scopes can designate general permissions, like read:document, but most applications provide access in the context of specific resources. You don’t typically get access to every resource. You are granted access to the resources relevant to your position and function. If you’re an engineer, you don’t need access to pitch decks, for example. Any real world scenario requires granularity you cannot get using scopes.
Defining a scope for each resource you have access to is impractical for two reasons. First, you may have access to thousands of resources, and they simply won’t fit into the access token (see 1). And second, computing all the resources you have access to at login time would be very expensive - potentially taking many seconds. Authentication is simply not an appropriate stage for doing this kind of fine-grained access control.
3. Stale permissions
The JWT is minted at login and typically lasts 1-24 hours. A lot can change in 24 hours. Applications that rely on scopes embedded in the JWT run a real risk of authorizing based on an outdated snapshot of a user’s privileges. Even if administrators change the user’s privileges the change will not take effect immediately, but only after the access token has expired and a new one is minted when it gets renewed.
This is in stark contrast to modern authorization services, like Aserto, which marries real-time access checks with a high-speed data fabric to ensure you never authorize over stale data. This fabric connects the control plane to locally deployed authorizers, so that changes to policies, user attributes, relationships, or resources are pushed in near real-time. That, along with real-time access checks, are how we ensure you make authorization decisions based on the most up-to-date information.
See it in action in the short video below:
4. Difficult to invalidate
Perhaps the biggest disadvantage of scopes embedded in JWTs is how difficult they are to invalidate. The user will continue to have privileges for as long as the access token is valid, even if an admin has explicitly revoked those privileges since the token was issued. This in turn leads to security holes and risks that most organizations simply cannot afford.
You can avoid these issues by using an external, purpose-built authorization service to manage application permissions and user privileges.
Granular permissions managed by an authorization service
An externalized authorization service offers many benefits over scopes embedded in access tokens. Modern authorization services provide fine-grained, policy-based, real-time control over protected resources, offering the following benefits:
- Fine-grained permissions: Fine-grained access controls allow for much more granular permissions than scopes. You can add resource-level access controls to your applications in minutes with Aserto.
- Real-time access checks: Adding an explicit authorization service eliminates the threat of stale permissions by letting the application check in real-time whether the user is still assigned the required permissions.
- Just-in-time access: Any change to user privileges can be transmitted to the authorization service and enforced in near real-time. For example, a “kill switch” in the form of a “disabled” flag that is set on a breached identity will shut off access immediately. This closes off security issues associated with blindly trusting scopes embedded in access tokens.
- Central management: You can centrally manage access to all your applications, and reuse policies across applications and services for consistency and ease of governance.
- Decouple application from IDP: Moving role-to-permission mapping from the IDP to the authorization service helps decouple the application from the identity provider, which now only needs to know about user-to-role mappings.
Conclusion
One of the earliest authorization patterns implemented bases access on OAuth 2.0 scopes that are embedded in access tokens issued by an identity provider. This method has multiple disadvantages, including lack of granularity, difficulty to invalidate, stale permissions, and scope explosion.
Access to your application is something you need to get right. You cannot risk providing access based on stale permissions, or waiting for a JWT to expire. You need resource-level controls, enforced in milliseconds. Aserto is an authorization service that makes it easy for developers to implement just that, in minutes instead of months.
We’d love to hear what you think about what we are building. Drop us a line or join our community Slack channel to engage with us directly. We look forward to it!
Related Content
Where should I enforce my authorization policy?
The journey of an application request includes a few opportunities to enforce your authorization logic. This guide helps you decide where and when.
Jul 29th, 2024
Isn't authorization part of authentication?
Authorization and authentication are often mistaken to be interchangeable concepts. In this post, we clarify the difference between the two.
Jan 27th, 2022
OAuth2 scopes are NOT permissions
OAuth2 scopes were never intended to be an authorization mechanism, and indeed are a bad idea when used as a substitute for a real authorization architecture.
May 3rd, 2021