OAuth and OpenID Connect standards rely on secure tokens to share authentication and authorization information. These tokens consist of claims, which are assertions made by the Authorization Server about a party (such as a user or client). Claims, in turn, are grouped into scopes to make it easier for clients to request sets of claims. For example, instead of requesting ten different claims, a client might ask for a scope to grant all of those claims. Together, claims and scopes create the functionality vital for clients to make proper authentication and authorization decisions.
Organizations often place considerable effort into properly designing the mapping of claims and scopes available in the system into authorization rules. Companies try to define the best sets of claims and scopes to handle authorization in APIs. Developers want to ensure that tokens contain enough data for the APIs to make informed authorization decisions. And as APIs grow and evolve, the set of claims and scopes might be altered to reflect new business areas or functionalities. Still, one area that tends to be overlooked, even when following best practices for claims and scopes, is that the content of a token should be viewed as a contract.
A Token's Content Is a Contract
The content of a token should be viewed as a contract or interface between the Authorization Server and the token consumer. Depending on the token type, the consumer is:
- The OAuth client in case of an ID token.
- The API or the API Gateway in case of an access token.
- The API Gateway or a reverse proxy (sometimes also the API) in case of a wrapper token.
The implication is rather obvious — as these consumers rely on the contents of the token, any change to the schema of that content should be viewed as a potentially breaking change. Introducing breaking changes to a system is the last thing a company wants.
This fact makes designing scopes and claims a bit more challenging, as any modifications made in a live system should be introduced with caution. Of course, not all changes are breaking, but the possibility should be considered. Especially in larger systems, where there are many consumers of tokens, this can get tricky. When I remove a claim from an access token, can I be sure that no API service relies on that claim to grant access to a resource? Or, will removing a value from a list of possible claim values now grant access to a resource that previously was not accessible with the given token?
It's worth remembering that a breaking change might not only mean that the code will throw exceptions, but that the authorization policies will break. There is a reason why Broken Authorizations rank first and fifth on the OWASP top 10 API vulnerabilities list.
The issue might become even more convoluted when dealing with third-party token consumers. If changing a token's content only affects first-party consumers, the company can update those consumers, leaving the business largely unaffected (though it still might be a tedious task in large enterprises). However, once third-party clients are taken into account, it becomes much harder to orchestrate all the necessary changes. Third-party integrations might use different technology stacks. Some are quite quirky and could break when new claims are added to the token (which normally would not be considered a breaking change). Third-party integrations could even break when the order of claims in a token is altered.
This issue with third-party consumers makes it even more important to keep the contract untouched. It also makes it vital for companies to limit the possibility of third-party clients misusing tokens. JSON Web Tokens (JWTs) are often used as a format for access tokens. The content of an access token is designed so that an API can perform proper authorization. However, developers tend to rely on the ability to freely read the contents of a JWT. Thus, once a third-party client starts using the contents of an access token in its implementation, it will be challenging to make any changes to such a token. Companies often might not even know that such integration exists and may accidentally disrupt a partner's business. To avoid this issue entirely, I recommend implementing the Phantom Token pattern, where clients receive opaque access tokens so that they don't rely on the token's content.
Implementing Token Issuance
Designing what claims and scopes are available in a system is an important task. Still, companies should not forget about properly designing the tokens, which eventually carry those scopes and claims. Architects and developers should remember the following when working with tokens.
Always Issue Tokens Centrally
There must only be one service in your application responsible for issuing tokens. This will be the Authorization Server most of the time, but sometimes there is a need to set up a different service responsible for tokens. Regardless, it must always be one service, as this affords the best control over token creation. If various services issue tokens, it is much more challenging to control the contents of those tokens.
Sometimes services must exchange one token for another to call other services in your ecosystem. Again, the exchange must go through the centralized token issuer, not through a separate specialized service.
The centralized token issuer might not have enough data to issue all token claims. Still, it should be this service's responsibility to gather this data during the process of token issuance. The token service can reach out to databases, other services, or even call external APIs, but eventually, it should assert the claims and sign them with secure keys.
Use Token Mappers
The centralized Token Service should be able to easily map claims to the different types of tokens it issues. Administrators should be able to effortlessly assign claims to scopes and decide the token type — an access token, an ID token, or a wrapper token. Claims about a user can also be returned from a user info endpoint, and this might be a different set from the claims in the ID token. Similarly, this process should be easily configurable. The Curity Identity Server's Token Designer is an example of such a mapper. A User Interface facilitates working with mappers, but it's also essential to be able to export the resulting configuration in a format that would enable version control and automated processing of changes (such as for static security analysis).
A configurable token mapper makes it much easier to ensure that no Personally Identifiable Information (PII) ends up in tokens, especially in ID tokens, which anyone can easily read. In cases where sensitive data needs to be returned to clients, it's usually better to have the user info endpoint return this data and keep it out of tokens altogether.
Governance Is Key
The solutions described above— centralizing your token service and utilizing token mappers — are crucial for implementing proper governance over the schema of a token. Companies should have processes that control what claims end up in tokens and procedures for introducing changes to token contents. Such governance will help limit broken client integrations due to changes in token contents. Proper governance will also help companies ensure no excess information is exposed in tokens, such as PII, or information about the company's infrastructure. What is more, auditing can be set up for tokens issued in this way. Claims asserted for a client and user can be easily audited together with the requested scopes and later queried, should such a need arise.
Once the access token contract is designed and fixed, it is straightforward to integrate an API with an Entitlement Management System, which provides centralized control over authorization. This can be done by sending the principal's claims to a system like Open Policy Agent.
OAuth and OpenID Connect tokens are vital standards to help maintain a high level of security. They are crucial to making proper authorization decisions by APIs, but one must think beyond authorization when designing tokens. Their contents schema should be viewed as a contract between the Authorization Server and clients, and companies should struggle not to break that contract. Changing the contents of a token without proper governance and caution can impact not only your business operations but also create security vulnerabilities, which attackers might promptly abuse. Once tokens are issued from a centralized service and proper mapping configurations are set, then implementing governance policies will become possible, and it will be easier to protect the business from disruptions.