Manual vs Generated OAuth Scopes: Lessons Learned While Building an Identity Platform
OAuth scopes have been part of authorization systems for years, and almost every identity provider manages them as independent objects. For small applications, this approach works perfectly.
However, after working on enterprise applications and identity systems for many years, I kept encountering the same problem: as the number of APIs, OAuth clients, and permissions grows, maintaining OAuth configuration gradually becomes more difficult than implementing the authorization logic itself.
This made me wonder: Should OAuth scopes really be managed manually, or are they simply another artifact that can be generated from the authorization model? To answer that question, I decided to experiment with a different approach while building my own identity platform.
Manual Scope Management
Most identity providers treat scopes as standalone entities. For example:
orders.readorders.writeusers.readusers.writereports.generate
Each scope is created manually, assigned to clients, documented, and maintained throughout the lifetime of the application.
Advantages
The biggest advantage is simplicity. Need another permission? Just create another scope.
Manual management also provides complete flexibility. Developers are free to define any naming convention and create scopes that don't necessarily correspond to a particular API or resource. For example:
legacy.exportpartner.synccrm.admin
This model also works extremely well for small systems. If an application has only a few APIs and a limited number of OAuth clients, manually maintaining twenty or thirty scopes is neither difficult nor time-consuming.
Finally, this is the model developers already know. Nearly every OAuth provider follows it, making it familiar and easy to understand.
Disadvantages
The disadvantages usually appear much later. As systems evolve, scopes gradually become disconnected from the APIs they were originally created for. An API may be removed while its scopes remain. Different teams introduce different naming conventions. Duplicate scopes appear. Some scopes are no longer assigned to any client but continue to exist.
Adding a new API often requires several manual steps:
- creating scopes
- assigning them to clients
- updating documentation
- verifying permissions
Eventually, even simple questions become difficult to answer. Why does this scope exist? Which API owns it? Is it still being used? Can it be safely removed? Over time, the identity server configuration gradually stops reflecting the actual system.
Generated Scope Management
While designing my identity platform, I wanted to try a different model. Instead of treating scopes as primary objects, I made them a result of the authorization model. The idea is simple:
Resource
โ
Permissions
โ
Generated OAuth Scopes
For example, defining a resource called Orders with two permissions:
- Read
- Write
automatically produces:
orders.readorders.write
Scopes are no longer managed directly. They are generated from the authorization model.
Advantages
The biggest benefit is having a single source of truth. Instead of maintaining resources, permissions, and scopes independently, only the authorization model needs to be maintained. Removing a resource automatically removes its generated scopes. Naming conventions become consistent across the system. Auditing becomes easier because every generated scope has a clear origin.
Adding a new API no longer requires manually creating every scope. As the system grows, configuration becomes easier to maintain because resources, permissions, and scopes remain synchronized automatically.
Disadvantages
This approach also has trade-offs. The authorization model becomes more sophisticated. Developers need to understand how scopes are generated instead of simply creating them.
Not every scope naturally belongs to a resource. Standard OAuth and OpenID Connect scopes such as:
openidprofileemailoffline_access
still need to exist independently.
Migration from an existing manually managed system can also be challenging. Finally, generated scopes reduce flexibility. If a project requires completely arbitrary scopes, the generation model must explicitly support them.
Which Approach Is Better?
I don't think there is a universally correct answer. For small applications, manual scope management is simple, explicit, and probably the right solution.
For larger systems involving dozens of APIs, hundreds of clients, and multiple development teams, maintaining OAuth configuration eventually becomes a problem of its own. That's where generating scopes from the authorization model starts to make sense. It reduces repetitive configuration, keeps resources and scopes synchronized, and makes the system easier to maintain over time.
This idea became one of the architectural decisions behind my identity platform, SolNeat Identity. I built it to explore whether this approach works well in practice, not because I believe manual scope management is wrong, but because I repeatedly encountered the same maintenance problems in growing systems.
I'm interested in hearing how others approach this challenge. Do you manage OAuth scopes manually? Have you experimented with generating them from resources or permissions? At what point, if ever, did manual scope management become difficult?
If you're interested in the implementation, you can find the project at https://id.solneat.com. I'm looking forward to your feedback and discussion.
Comments
No comments yet. Start the discussion.