In our last post we described User Data intricacies and next we need to clarify how we will work with this data in our UIs and APIs.
This page is about getting the API architecture right, so that APIs can deal with tokens and claims in a high performance and future proof manner.
The 3 main API Scenarios
Let’s briefly think ahead to when you have a more complete OAuth architecture. You may then have 3 categories of API caller:
|API Caller||OAuth Flow|
|UI Client||Implicit / Authorization Code|
|Back End Service||Client Credentials|
|Federated UI Client||JWT Bearer Token Exchange|
In a later post we will cover what I term ‘Federated UI Clients‘, which are those of a Business Partner. These have their own authentication but we want them to seamlessly integrate with your company’s components.
Goal: Consistent API Coding Model
You are likely to want to want your API Architecture to work the same regardless of the type of caller, perhaps by doing this:
- Identify the Calling User and Calling Application from the token
- Apply Authorization + Business Rules based on the Caller Identity
Goal: High Performance APIs
Some processing needs to be applied on Every Single API request, which might include the following if you have a multi-tenant system:
- Validating the Access Token
- Getting the User Id
- Getting the Company Id associated to the user
You will want to perform the above operations without any noticeable impact on performance. We will use Simple Claims Caching for this.
Goal: Extensible Claims Handling in UIs and APIs
Once you have a high performance API architecture you are likely to want to add extra domain specific user data points to particular APIs. You may then want to return these custom data points to particular UIs.
Goal: Portable APIs
Most companies will want to avoid coding their APIs in a manner that locks them into a particular Authorization Server’s vendor behavior. It is good to be able to move vendors with minimal API code changes.
Access Token Differences
A UI will call your API with a token that looks something like this, and a User Id will usually be present in the ‘sub’ claim:
A Back End Service might call your API with a token that looks like this, where the ‘client_id’ claim represents an application identity:
Access Token Customization Limitations
It is common for vendors to allow access tokens to be customized, so that as well as the subject / user id claims you can include additional data points:
However, you may find that the vendor customization options are different for the 3 categories of API caller mentioned above:
- Custom attributes may be supported in some cases but not others
- Custom scopes may be supported in some cases but not others
Access Token Design Preferences
My preferences around access tokens are summarized in the below table.
|Minimal and Portable||The token contains only the User Id, protocol claims and scopes, and other details are looked up via an API|
|Validation via Introspection||Less code complexity and reduced scope for operational problems when token signing keys are replaced|
|JWT Format||Interoperable with business partners,who can verify your tokens as proof of authentication in some scenarios|
|Extensible User Data||Access tokens fit into an overall design for API user data with the best separation of concerns|
In Memory Token Validation
Most online code samples validate JWTs in memory, which is typically implemented via a security library that performs these actions:
- Downloads token signing keys from the Authorization Server
- Caches token signing keys in memory
- Verifies the token’s signature / issuer / audience / expiry
This is a perfectly valid solution and may work fine for many companies, though I prefer the way introspection externalizes security from my code.
Token Signing Administration
One of my concerns about in memory token validation is that IT Administrator changes can be more difficult:
- If an IT Administrator renews a Token Signing Certificate you need to ensure that all of your company’s APIs update their cached keys
- If an IT Administrator wants to change the Token Signing Algorithm you may need to update code in all our company’s APIs
Introspection enables you to manage operational aspects more easily, since the Authorization Server is the only party that ever reads the access tokens.
Claims Caching in APIs
I have found the following design option to provide the best separation of concerns, the most extensible and the best from an operational viewpoint.
The first time an access token is received on a particular server, you validate the token to get the User Id, then look up other data points, such as Name, Email, Tenant Id, Role(s), then cache all of these data points together:
On all subsequent calls with the same access token, the claims needed can be returned very efficiently from the API’s memory cache:
What I like most about this is that different API owners from different teams can extend their own API claims cache however they want. There is no impact on the central team, and responsibilities are in the right places.
UIs and Displaying User Data
Your UIs can then easily get Central or Product Specific User Data Points just by calling an API method such as /user/current with an access token.
Note that an important factor is to cache claims keyed against the access token. This enables users to force their UI to use up to date claims, if for example the user’s rights have been changed by an administrator:
- Start a new login session so that the UI gets a new Access Token
- The UI sends the new access token to the API
- The API has to do a new data lookup and return up to date User Data
Claims Caching Implementation
There is an Identity Server Code Sample that show how to implement this design for .Net code:
A key aspect of the cache entry is that it is given a time to live equal to the access token’s expiry time, which is contained in the ‘exp’ field of the JWT.
Caching and Token Lifetime
This design assumes you are dealing with short lived access tokens that will soon expire, so that caching them does not increase attack windows.
Cache Memory Size?
If your API receives calls from a huge number of active users you may need to rethink the above, such as using an Out of Process Cache.
This blog is using a Reference Token Model, which is more commonly used when the tokens are in a Reference Token Format. This format is vendor specific and ‘points’ to state stored on the Authorization Server:
You can meet all of the above goals equally well with Reference Tokens, if your Authorization Server supports them.
My preference is to use JWTs however, since they have better options for using Newer Token Exchange Flows with business partners.
Most Corporate APIs will be hosted behind a Load Balancer, and in some cases more advanced middleware such as an API Gateway will be used.
Some API Gateways provide features to introspect reference tokens, then forward a JWT to your API, then cache results.
In this type of setup you would need to adapt our solution, but your API’s data requirements are likely to remain the same:
- Getting token data to identify the caller
- Getting other central user data needed by your API
- Getting domain specific user data needed by your API
- Avoid needing to retrieve this data on every single API request
Where Are We?
Our access tokens are small, confidential and portable. Our APIs can receive them efficiently and API authorization works the same for all OAuth scenarios.
Central and domain specific user data is nicely separated for both UIs and APIs. Introspection helps externalise security and works well operationally.