Introducing The Usher: an Authorization Server
Patryk Laurent
Posted on December 22, 2021
The Usher is an authorization server that issues tokens for customer access to applications, APIs, and data, based on SKUs they have purchased. Today, we are pleased to announce we are open sourcing The Usher!
This blog post introduces The Usher, how it works, and some of the ways we use it in our companies across the DMGT portfolio. However if you'd like to dive right in, clone The Usher Server repo and issue a docker-compose up
!
What is The Usher and what is Authorization?
Authorization refers to managing access to resources via granted permissions. We view authorization as one process interdependent on several other processes, such as: authenticating; retrieving information about purchased subscriptions or products (SKUs); assessing the persona's intended access; and enforcing resource access policies.
The Usher focuses on just the authorization piece. At runtime The Usher is a microservice — deployable as a cloud function with a database — that issues access tokens to personas authenticated by an identity provider (IdP), like Auth0 or Azure Active Directory. The granted permissions are borne in the issued token's claims, in the form of OAuth scopes (more on this later; on terminology see footnote[1]).
The set of permissions available to a persona is represented in The Usher's database. This database can be populated directly or as is our usage, synchronized with a CRM system like Salesforce (e.g., via Heroku Connect). In the latter scenario, available permissions are determined by SKUs purchased by the persona or their organization. The permissions typically come from roles although they can be associated directly with personas. The Usher also supports a feature called user_context
which lets a persona access distinct sets of roles and permissions, for example when acting as an employee of a different company, for support staffs or call centers, or as an administrator.
Design
Whereas authentication systems and sales databases are essentially solved software problems in 2021, authorization and policy enforcement remain an open area of investigation and exploration. This is highlighted by numerous recent articles, debates, and presentations like "Why Authorization is Hard" and "Authorization: its history and its state in 2021", among others.
One of our main goals in designing The Usher was to help our companies to rapidly develop their commercial software offerings while adopting best practices. Last year, we noted that many of our operating companies were discussing authorization solutions, with many overlapping requirements. Yet, these requirements were not adequately addressed by existing solutions or services. Speaking to the operating companies’ lead developers and CTOs led to a feature list that pointed to a design with a very small architectural footprint. The Usher would emphasize simplicity, modularity, and when needed — composability with other services.
The Usher’s design tenets are as follows:
- To Separate Authorization from Authentication. The Usher draws a sharp distinction between authorization and identity (authentication). The Usher knows nothing about usernames, passwords, or 2FA; in short, it cannot be used to manage identity. All of our businesses (or their customers) already have identities managed via some identity provider.
- To Separate Authorization from Policy Enforcement. The Usher is entirely unopinionated about how access policies are to be implemented and enforced in the wide range of backend resources, languages, and environments we use in our businesses. We did note with interest that significant progress has been recently made on general policy enforcement frameworks; see OpenPolicyAgent/Rego, Aserto, and others. It should be possible to use these with tokens issues by The Usher. (Incidentally, we are discussing the possibility of integrating a policy engine into The Usher to enable attributes or other context to influence scopes in the issued tokens; if this interests you, please watch this project for news).
-
To Adopt OAuth2 Scopes for Permissions. The Usher adopts the OAuth2 framework for authorization, with a few strong simplifying assumptions to address our use cases. For instance, the Usher assumes that by logging into an application, the persona implicitly authorizes the application to access backend resources on its behalf. No separate step of selecting or delegating permissions is required. (However -- see the note about the optional use of a feature we call
user_context
, described below).
What about OAuth2 Scopes not being Permissions?
Adopting OAuth2 scopes for permissions is probably the most controversial design choice in The Usher (e.g., see Scopes are not Permissions and On the Nature of OAuth2 Scopes). Despite the objections to doing so, we have found OAuth2 token-based authorization to work very well in our businesses; perhaps this is because of the nature of our products and subscriptions. When closely examine, some of the objections do not really apply, and for the others there are reasonable workarounds or additional services that we compose with The Usher.
To briefly review, here are the objections to using OAuth2 scopes as permissions in the above articles, and our stance: "Scope explosion" refers to the combinatorial explosion of resource types (document, message, stock portfolio) when crossed with operation types (read, write, delete). We address this by not being overly fine-grained with our roles and permissions. This turns out to be a good design decision in actual practice for many businesses who are using The Usher to permission access based on SKUs; fine-grained permissions often confuse both developers and customers.
The issue of “stale permissions” isn't an issue in our case; the typical lifetime of our access tokens is reasonable with respect to renewal or expiration of our subscription offerings -- it's not a big deal to the business if a customer gets less than one day of free access after the last day of their subscription. Furthermore, if new permissions are added for a customer, a reasonable UX is to log out and log back in, or force a refresh of their access tokens through the app. Thus, we don't use ultra-short-lived tokens, and don't run into performance issues.
Finally, although it is conceded that “OAuth2 scopes can designate a general permission like read:document
,” there is a concern that many applications need permissions to specific resources (i.e., row-level access control). This “lack of resource context” is something that The Usher does not explicitly address; so long as the resources are coarse, the appropriate context can be embedded in the scopes. For example, a scope of self:mailbox:read
would refer to a specific resource in the context of the current user, and this differs from a putative admin:mailbox:read
scope which might give administrative access to all user mailboxes.
How we use The Usher
Overall, we find it beneficial to use a simple decentralized, token-based authorization system. The Usher helps us accelerate software and systems development across our operating companies, while adopting secure best practices. It also enables our sales organizations to create new products which can be sold directly to a given customer, with no additional work by the technology team. They achieve this by bundling SKUs that map to a streamlined set of roles and permissions. Of course, this required upfront work in designing the SKUs/entitlements -- in some cases, paring down an overly complex legacy entitlements system, and simplifying the offerings. This is a product management task that cannot be ignored, but is out of scope for The Usher itself.
The Usher's authorization model, based on OAuth2 scopes, does not exhaustively address all of our operating companies’ requirements for access control. However, as it adheres to RESTful standards, it works well as a component in a larger architecture. Our operating companies can and do design other services to use in conjunction with The Usher, such as a “provisioning” server. For example, in one company’s product, customers purchase plans (SKUs) to get data from number of regions across the country. One plan specifies the user can subscribe to at most 5 regions, and The Usher mediates authorization for adding these. But the specific selected regions are not stored as permissions in The Usher’s database – rather they are stored in a user “provisioning“ database. The provisioning database is checked only by those endpoints that need to check it, preventing performance issues. This pattern could also be used to address access to a consumable resource. This overall design avoids the problem with a combinatorial explosion of permissions overwhelming tokens from The Usher.
The Usher has some features and APIs aimed at making it easier to design and implement client applications. First, it will only return the scopes relevant for the application issuing the request. This mitigates the issue of tokens being too large for headers when the client uses them in its requests. Also, the list of roles in the returned token is limited to only those that cover the scoped permissions — the client can inspect this to determine what user interface to show or hide. A client can query The Usher for the full list of applications to which the identified user has access (i.e., any permissions) — this can help when developing portals that allow the user to select from a list of available applications. The Usher also provides endpoints that a client can query to get the full list of roles and permissions which have been granted to the identified user.
The Usher can be conveniently deployed in two ways, as a severless cloud function (AWS Lambda, Google Cloud Functions) or as a containerized app (Azure Container Instances, Google Cloud Run) depending on requirements.
What else is out there?
When we first started thinking about the authorization problem, we did not want to write anything ourselves. At that time, we could not find a fast and lightweight service and thus The Usher was born. Since then, there has been an increased level of activity. What follows are other systems which we think fit in the same space as The Usher:
- https://www.ory.sh/docs/ecosystem/projects/
- https://curity.io/product/token-service/
- https://blog.openpolicyagent.org/write-policy-in-opa-enforce-policy-in-sql-d9d24db93bf4
- Auth0.com authorization extension
- Aserto
Features not yet implemented in The Usher (Roadmap)
In keeping with the Unix philosophy, we intend to keep The Usher a small, server focused just on the infrastructure required to issue access tokens. However, there are features that would be beneficial to add to The Usher without too much complexity. Currently there is a partial implementation of Groups functionality, which could be used to map users to roles and permissions based on their group membership (based on a claim in the identity provider token). Below is a list of other features that are not yet implemented but may be added based on usage requirements:
- Batch assignment of Roles. For some products, assigning individual roles to users is too fine grained. The Groups capability (in progress) could be leveraged to make batch assignment of roles easier for sales.
- Admin API. Currently roles and permission assignments are only administered through Salesforce (via Heroku Connect) or direct interaction with The Usher’s database. A full admin API would make it easier to synchronize with a wider range of services.
- Signing key rotation (automated and/or via API endpoint). Currently signing keys need to be manually rotated when, or before, they expire.
- Integration with a policy engine like OPA/Rego for context/attribute-aware scopes in issued tokens. This would allow for direct integration with a provisioning server, to handle, e.g., consumable resources.
- Sessions/refresh tokens per persona-client, or per persona-device. The Usher currently only supports a single session per persona. If the user were to log into the same application from another device and request more restricted or different scopes, the refreshed token on the first device will reflect this restricted scope. This might be unexpected UX.
- Serverless database layer (e.g., Firestore or DynamoDB). The only layer implemented at the moment is a PostgreSQL layer. A serverless database layer would pair nicely with serverless deployments of The Usher server.
Invitation to Join Us
Do you have businesses that need to implement authorization in a minimalist and modular way, leaving identity to another service? If so, we encourage you to examine The Usher as a possible component in your architectures. Pull requests and comments are welcome.
Footnotes
[1] A note on Permissions versus Scope. Permissions are all of the privileges that a user has; these are contained in The Usher’s database. Scopes, here, are the subset of those permissions that are granted to a client application, generally by virtue of the user logging in and using that application. The Usher supports two ways for further refined scoping: the client application can specify desired scopes when requesting a token from The Usher; the other way is to use a parameter called user_context
which selects among alternative persona rows with the same sub
claim.
References
- Aserto (2021). "OAuth2 Scopes are NOT Permissions” (May 3, 2021) https://aserto.medium.com/oauth2-scopes-are-not-permissions-575120f0e5fb
- Scott, S. (2021). “Why Authorization is Hard”. (Sept 15, 2021). https://www.osohq.com/post/why-authorization-is-hard
- https://blog.aserto.com/blog/2021/04/authorization-is-not-authentication/
- Bertocci, V. (2018) “On The Nature of OAuth2’s Scopes” https://auth0.com/blog/on-the-nature-of-oauth2-scopes/ (Sept 5, 2018)
- Brossard, D. (Salesforce) & Schenkelman, D. (Auth0) (2021). “Authorization: its history and its state in 2021.” https://m.youtube.com/watch?v=bzRYGh5Zo0Q
Posted on December 22, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.