Identity Propagation in an API Gateway Architecture
The power of end-to-end user security context with APIs
As enterprises continue to expand their usage of APIs, the need to keep those APIs secure increases as well. One way to bolster security and improve auditing and authentication is the transmission of an authenticated user’s security context from the front end of a request pipeline, beyond the API gateway, and all the way to the back-end implementation of an API or service.
End-to-end transmission of an authenticated user’s security context benefits API-based systems by enhancing overall security, eliminating the use of generic (privileged) accounts, providing a secure audit mechanism (of traffic traversing the system), and supporting advanced authentication use cases.
This pattern can in some instances cause some complexity, but, as you’ll read in this post, it provides some valuable benefits.
End-to-end identity propagationIn most architectures, the propagation of the end user security context tends to stop at the API gateway layer, and a more generic security mechanism such as Mutual Auth SSL or a service account (with basic authentication) steps in to secure the API provider layer.
However, there are several benefits to propagating the authenticated user security context all the way to the API provider:
- Support for processing Attribute Based Access Control (ABAC) authorization decisions based upon the end-user identity at the API provider.
- Avoidance of generic service accounts with super-user privileges to connect to the backend.
- Provision of a secure audit mechanism for the user who initiated requests that hit each layer of the infrastructure.
- Maintenance of certain compliance requirements
- Value in SOA and microservice architecture implementations
- Support for authentication protocol translation
Token exchange functionalityIf there is an intermediary between the front-end (such as a native mobile app or single-page web app) and the API implementation, such as an API gateway, this pattern becomes more complex, but is still possible.
This is where token exchange functionality such as that defined by the OAuth2 Token Exchange protocol (grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer with requested_token_use=on_behalf_of; in the most recent spec drafts "on_behalf_of" was changed to "subject_token"), the WS-Trust (active clients with On-Behalf-Of and ActAs) spec, and Windows Kerberos extensions (Protocol Transition and Constrained Delegation, which is based upon Kerberos) can be used.
It’s important to note that this activity requires the intermediary to play an active role in making this specific request; the identity provider must grant explicit rights to the intermediary to be allowed to make this request.
This token exchange functionality essentially enables an actor to exchange a token (such as SAML2 or JWT) with one audience (presumably its own) for a token that describes the same user, but with a different audience from an identity provider. This is done in a secure manner, meaning there are significant limits placed upon which audience the new token may reference.
Two common forms of this token exchange are delegation, where the protocol provides a concept of the intermediary that is making the call (such as the WS-Trust ActAs), and impersonation, where the protocol provides no concept of the intermediary (such as WS-Trust On-Behalf-Of). The OAuth2 Token Exchange spec defines similar concepts for OAuth2 and OpenID Connect (OIDC); however, it uses the terms to mean the opposite of how they are used here.
The API gateway scenarioThis pattern is an evolution of a similar pattern used in the SOA world. In 2011, I did a presentation about using this pattern for SOA implementations with SOAP actors, SOAP web services, SAML2, WS-Trust, and WS-Security. The emergence of APIs and API gateways merged the concepts used in the SOA world to enable front ends (such as native mobile applications and SPA applications) to communicate with the backend and what (SOAP web services) was used by backend intermediaries (ESBs) to communicate with the service implementation layer.
Now, the entire request/response pipeline, from the front-end consumer to the backend implementation, can use RESTful APIs.
Let's make some assumptions and set some requirements for this scenario:
- The access token is a digitally signed bearer token (JWT).
- All system actors are part of the same security realm (in other words, they use the same identity provider). This identity provider can authenticate all end users of the front-end application.
- Every system actor (API gateway, API provider, and, possibly, the datastore) must validate the identity token (authenticate the request) attached to an incoming request.
- This token validation includes audience restriction enforcement, which further ensures the token is only used where it is supposed to be (the granularity of the audience depends on several factors, including desired reusability of the token, resource authorization policy, and organization security standards).
- All communication occurs over TLS v1.2 or better.
- All communication occurs with RESTful APIs using JSON as the payloads (where appropriate)
- All security details will be implemented with spec-based security (more on that below)
- For added protection, use mutually authenticated SSL between actors where the server component only accepts connections from a single actor (between the API gateway and API provider, for example). This assumes that a single client certificate describes a single system or possibly a single node within a system. There shouldn’t be sharing of certificates across systems.
OAuth2 and OpenID ConnectSo, what specs can be used to implement this security model? If you've read my earlier posts, then you might guess that the answer lies within OAuth2 and OpenID Connect—and you’d be correct.
As outlined in the diagram above, the result of that initial authentication includes an OIDC ID token and OAuth2 access token. The ID token must be validated per the spec, then information contained in it can be relied upon to describe the authenticated user.
If additional information is needed, the mobile app can make a call to the IdP OIDC UserInfo Endpoint to obtain more information about that user. Additionally, the OAuth2 access token that is provided should be cached on the mobile app and included with the authorization header of each API request per the OAuth2 Bearer Token Usage spec. When the mobile app needs data from the backend, it makes an API request to the API gateway.
Now, if the OAuth2 access token is also a JWT token, that makes the downstream authentication (access token validation by the API gateway) easier. If it is an opaque token, then the system actor must be able to support passing the access token into an IdP OAuth2 Introspection Endpoint to validate the token.
Securing each step of the request’s journeyThe mobile app will make API calls to the API gateway; identity information will be passed from the mobile app to the API gateway layer. When the request arrives at the API gateway, the access token must be extracted from the request and validated. This validation step could be performed locally, if the token is a JWT, or could be done by querying the identity provider's OAuth2 Introspection Endpoint or other identy provider-specific mechanism. If necessary, a call can be made to the OIDC UserInfo Endpoint.
Some type of coarse-grained authorization decision will likely be made at this point, the details of which are beyond the scope of this conversation. Finally, the access token that was passed into the API gateway with the request needs to be replaced with an access token whose scope matches the downstream actor’s (API provider’s) scope.
This is accomplished with a Token Exchange Grant call defined by the OAuth2 Token Exchange spec. The new access token is placed into the API request (replacing the existing token) and that request is routed to the API provider layer.
When the request arrives at the API layer, the access token is extracted and validated in the same fashion as the input token at the API gateway layer. Likewise, the UserInfo endpoint can be queried for additional information, a fine-grained authorization decision can be enforced, and possibly a new downstream token obtained for the datasource tier. For the last component, this assumes that the datastore layer can process an OAuth2 access token.
The request that is sent to the datastore is almost certainly going to look different than the one that was sent to the API provider. There may also be multiple datasource queries for a single API request. Implementing the OAuth2 access token check at the datastore will likely require a custom authentication module on the target datastore in order to implement the desired behavior.
The authentication and identity propagation model described here isn’t applicable if:
- the end user's security context isn’t extended to the backend
- generic service accounts are used
- OAuth2 Client Credential Grant is used
- any of the other assumptions listed earlier in this post are not true
Furthermore, using a spec-based approach to implement a complex identity pattern such as end-to-end identity propagation provides a layer of insulation between your solution and the identity provider vendor, making it easier to change out the identity provider later if needed.