All posts

Authorization Decision Input Parameters

I was working with a developer not so long ago and it was explained to me that a caching layer had been added to a services layer (API Provider) to make the authorization (Fine Grained Authorization, FGA) decision more efficient for subsequent API calls. In particular, the cache contains data retrieved from the application database that is used to make the authorization decisions. Other information that was needed for authorization decisions either came from a JWT acting as an OAuth2 Access Token or queries to the Identity Provider (think OIDC UserInfo Endpoint). This caching strategy is more-or-less a requirement to build a well-performing application. However, as I dug into the implementation deeper, I found that the developer had created a self-signed JWT token that was set as a cookie with the expectation that the API Consumer would submit the token on all subsequent requests. This new JWT would be in addition to the OAuth2 Access Token that already had to be submitted on every API call. DON’T DO THIS! Recall, that JWT acting as the OAuth2 Access Token can contain claims ranging from the username to everything the User Repository knows about the user. While the application data, generally, cannot be placed into this JWT issued by the Identity Provider and one must always be mindful of not exposing sensative data in a manner that exposes it to outside actors, it does provide an existing mechanism that can be used as a unique cache key for the authorization data cache.

Adding an additional token to this scenario is not in line with the spirit of the security models I’ve been describing in blog posts for the past few years. More than that, it is superfelous. Any data that needs to be cached by the PDP needs to be mapped to the JWT Access Token to which the authorization decision belongs. Using a hashing algorithm on the JWT Access Token such as SHA2 should be sufficient to securely map tokens to the supporting authorization data and resulting decision. All data needed to make authorization decisions should either come:

  • Directly from claims in the JWT acting as an OAuth2 Access Token.
  • From the user attributes stored in the user repository (via OIDC UserInfo Endpoint, XACML PIP (Policy Information Point), or similar information endpoint) that belong to the user described in the JWT acting as an OAuth2 Access Token.
  • From the application database and must map to the user described in the OAuth2 Access Token.

The key point in all those options is that the identity that all this data maps to is defined solely by the authenticated identity (and claims) described in the JWT acting as an OAuth2 Access Token.

OAuth2 Access Token: Original Purpose

At this juncture, I should mention that the original purpose of the OAuth2 Access Token was not to convey an authenticated identity, but rather to represent the permissions granted to a particular application by a resource owner (probably, but not necessarily, the authenticated user) on a resource (such as an API or many other possible things). From the OAuth2 RFC, we have:

Access tokens are credentials used to access protected resources. Anaccess token is a string representing an authorization issued to the client. The string is usually opaque to the client. Tokensrepresent specific scopes and durations of access, granted by theresource owner, and enforced by the resource server and authorization server.

Clearly, that doesn’t say anything about describing an authenticated user. This capability is introduced by using a JWT as an OAuth2 Access Token. Most (but, not all) Identity Providers have implemented OAuth2 in this manner today.

Coarse Grained vs. Fine Grained Authorization of API Calls

In the example above, we were discussing Fine Grained Authorization decisions on an API Provider that is presumably implementing an API that is advertised on an API Gateway. Similar pieces of data are needed on both the API Gateway and the API Provider. I’ve described the concept in other blog posts, but I will rehash it hear for completeness of this post.

The API Gateway cannot know all those application API Authorization Policy-specific details that will bubble up into your APIs’ authorization policies. Or, it may have access to the policies (think XACML), but probably doesn’t have access to all the data that is needed to evaluate the policy. So, I advocate two layers of authorization enforcement in the API run-time: one at the API Gateway layer that is usually a form of Role-Based Access Control (RBAC), described above, called Coarse Grained Authorization (CGA) and one built into the application logic that has full access to the application context and all data needed to make detailed authorization decisions (think the application database) called Fine Grained Authorization (FGA). The terms CGA and FGA have been criticized because they are vague and tend to mean wildly different things across organizations. Here, I am using the following simple definition. CGA is the RBAC decision that is enforced at the API Gateway layer; FGA is anything more complicated than this that is moved back into application code.

Given all of that, if something like XACML or ALFA is used at the API Gateway layer, a very rich authorization policy could be defined across a wide variety of situations. However, you also risk making the API Gateway more complex than it was originally intended. It is also unlikely that the API Gateway is ever going to have access to the application-specific datastores that would be needed to make the most complex authorization decisions. There will be more on XACML and ALFA in another post. Nevertheless, XACML (or ALFA) could be used to define both the CGA and FGA policies — these policies will just be evaluated at different layers.

You can see how these authorization concepts relate to the many other concepts I write about here.

API Authorization Decision vs Token Issuance

The OIDC and OAuth2 specs support an authorization decision concept at the time of token issuance. Namely, should the authorization be allowed (ie, the token issued)? This decision could be based upon predefined delegation rules (can application A obtain a token for resource B?), whether the calling application is authorized, or whether the end user is is authorized. The OAuth2 spec on its own supports the decision of whether an Access Token should be issued; the OIDC spec extends the decision to the application itself with an ID Token. For application access, the presence of an ID Token with the appropriate audience and scope really only determines whether the user is authenticated.

In contrast, the authorization decisions made at the API Gateway or API Provider layer protect the API endpoints (server side resource) and are evaluated when each API requeest is received.

Some Assumptions

  • Mobile or Single Page Application is acting as an API Consumer.
  • OpenID Connect is being used to authenticate the end user and obtain the needed tokens.
  • The API Consumer is making API calls through an API Gateway.
  • The API Gateway handles authentication and Coarse Grained Authorization decisions of API calls.
  • The API Provider (behind the API Gateway) performs Fine Grained Authorization decisions (if needed).
  • All of the other assumptions presented in “SECURELY USING THE OIDC AUTHORIZATION CODE FLOW AND A PUBLIC CLIENT WITH SINGLE PAGE APPLICATIONS” post are also true.

Additional Thoughts

  • A stateless security model based on a JWT acting as an OAuth2 Access Token should be used. Let’s call it a requirement for the IdP.
  • What is being authenticated and authorized at the API Gateway layer could be the application or the end user. You need to understand which it is in your use cases. You may have to support both on the same API for different API consumers.
  • Deciding whether an authorization decision is a CGA or FGA decision will often have gray area. A judgement call will sometimes be needed.

Summary

All security session tracking between the application and the API Gateway should be done with the OAuth2 Access Token. The Access Token will be a JWT. All information that is used to make authorization decisions should extend from claims in the JWT. Caching of authorization data (not already in the Access Token) and/or the decision will almost certainly be required.

Image: ferns / liz west

Originally published on Medium.