Demo: Apigee Edge OAuth2 Debugging
Robert Broeckelmann is a principal consultant at Levvel. He focuses on API management, integration, and identity–especially where these three intersect. He’s worked with clients across the financial, healthcare, and retail fields. Lately, he’s been working with Apigee Edge and WebSphere DataPower.
As I’ve described in previous posts, OAuth2 and OpenID Connect (OIDC) have emerged as the de facto standards for securing APIs (for authentication and authorization). Apigee Edge provides an out-of-the-box OAuth2 implementation.
It’s a common pattern to “wrap” a third-party identity provider (IdP) with the Apigee OAuth2 functionality. In other words, Apigee Edge can act as an OAuth2 provider while an external IdP is employed to provide end-user authentication services. In this post, we’ll explore assumptions and requirements for a real-world application.
We are going to introduce two Apigee API proxies; one that implements our OAuth2 provider as a wrapper around a third-party IdP using the OIDC spec and one that protects an API with OAuth2 security. Then, we’ll use my OAuth2 + OIDC Debugger to demonstrate the authorization code grant and refresh token call.
To keep the size of this post at a reasonable length, I’ve put most of the technical details into Github repositories and supporting blog posts:
- OAuth2-Protected API proxy protecting an API
- OAuth2 provider API proxy
- OAuth2 + OIDC Debugger
- Configure and run the Apigee Edge OAuth2 example
If you want to move on to those technical details, go ahead and look at the steps outlined in the fourth link.
Follow the specs
Apigee Edge provides the building blocks of an OAuth2 authorization server that are meant to be assembled by a skilled practitioner in whatever configuration is needed for the given use case.
While flexible, this gives one a lot of rope with which to hang one’s self; as always, any real-world applications of this technology should adhere to the relevant specifications and undergo proper penetration testing.
I’ve already built some non-spec-defined features into the debugger to deal with implementation details of other OAuth2 (and OIDC) implementations. All of the implementations closely followed the specs, including:
- User-agent (browser) interacts with the authorization endpoint as defined in the specification. The call will include all required parameters (as well as some optional parameters, and maybe some proprietary parameters).
- Application (client) interacts with the token endpoint as defined in the specification. The call will include all required parameters (and some optional parameters, and, again, perhaps some proprietary parameters).
- The login sequence is initiated by the user-agent by making the call to the authorization endpoint. This endpoint either does a redirect to a separate authentication workflow endpoint or returns a login form. The exact details of how this works are beyond the scope of the specification.
- There is some type of trust relationship established between the authorization endpoint and authentication workflow endpoint—typically implemented with a security-session tracking cookie.
- For the OAuth2 authorization code grant, OAuth2 implicit grant, and all OIDC authentication flows, the IdP serves the authentication workflow.
The Apigee OAuth2 examples that involve end-user authentication generally involve Apigee Edge acting as an OAuth2 provider and a third-party IdP handling the end-user authentication.
There are several ways of integrating these two concepts. We can’t cover them all here. We have this one that is Apigee’s official example of the authorization code grant. It is discussed in further detail here. This example has the (server-side) client application making an initial request going to the third-party identity provider (simulated by an Apigee API proxy). It is similar to the OAuth2 protocol, but isn’t spec-compliant.
An OIDC example
So I looked around for another OAuth2 authorization code grant example from Apigee that looked a bit more like what I was used to seeing. I found this example that has the same high-level pattern (only OIDC authorization code flow) with Apigee as the OAuth2 provider and a third-party IdP (PingFederate) as the IdP.
This OIDC example wraps the third-party IdP response in an Apigee-issued OAuth2 access token that is returned to the calling client application. It accomplishes this by impersonating the original client application at the API proxy (Apigee Edge layer) during its interaction with the third-party IdP.
This implementation has the initial spec-defined OAuth2 authorization code grant call to the authorization endpoint, but the registered endpoint in the third-party IdP is the Apigee API proxy. The end result of this is that the client application doesn’t actually make the call to the token endpoint. One could imagine how someone would view this as beneficial and easier from the perspective of the client app developer.
Actually, what this example is doing is similar to one of OIDC hybrid flow variants (no interaction with the token endpoint from the client’s perspective), but that doesn’t match up with the response_type in use. This, also, is not meeting the OIDC and OAuth2 spec compliance we need in the example that we are going to use.
Key design principles
So, I created my own implementation that used the following design principles:
- A third-party IdP is responsible for authenticating the end user and applications. In our example, Red Hat SSO v7.1 is acting as the IdP responsible for authenticating end users and the applications.
- All clients send OAuth2 requests to an API proxy that wraps interaction with the third-party IdP.
- The third-party IdP has no concept of the API gateway (or proxy) that is acting as an intermediary. The most secure implementation of this pattern would be to ensure the third-party IdP has a clear understanding of the API gateway and the apps. However, modeling the delegation rules between these actors is far more complex than pretending that the API gateway doesn’t exist in the IdP. So, for now, we’re not going to worry about that detail.
- The authorization endpoint on Apigee returns a redirect to the third-party IdP authorization endpoint (using the same query parameters). This doesn’t explicitly hide the third-party IdP from the client application, which would likely be preferable in most situations—let’s call this the author taking a shortcut that could be easily resolved if properly motivated.
- The authorization codes, refresh tokens, and access tokens issued to the client applications are generated by Apigee Edge, but issued after validating user and application credentials against the third-party IdP.
- The OIDC protocol is used to integrate with the Red Hat SSO for the authorization code grant. This gives us access to the UserInfo endpoint to retrieve information about the user.
- The OAuth2 protocol is used to integrate with Red Hat SSO for the client credentials grant.
- The cached refresh token on Apigee is only held for eight hours. After this, the user would have to start a new session by logging in again.
- There are numerous other timeout considerations across access tokens, refresh tokens, and sessions on the IdP that should be considered for real-world usage. That’s beyond the scope of this post (and the given example).
We also assume that the authorization code grant and client credentials grant have been implemented in Apigee for the purposes of this example. The other OAuth2 grants can be implemented easily enough using the building blocks from these two.
Given all of that, we arrive at this API proxy (available on GitHub) that wraps a third-party, OIDC-compliant IdP. There is a pre-built API proxy bundle available here if you want to get started very quickly.
Likewise, I created this API proxy that will protect a backend API using Apigee’s out-of-the-box OAuth2 access token validation.
The details of how to build, deploy, and test these proxies can be found here.
The basic interaction between the actors is described by this diagram:
The steps are:
- Load OAuth2 + OIDC debugger UI.
- Send request to Apigee OAuth2 authorization endpoint (advertised by the OAuth2 wrapper API proxy) to kick off the authorization code grant.
- User is redirected to third-party IdP OAuth2 authorization endpoint.
- User authenticates against IdP (involves interaction with IdP login workflow).
- Authorization code is returned via redirect to redirect URI; results in authorization code being available to debugger UI.
- The debugger UI makes a call to its backend with the token endpoint parameters that must be given to the Apigee OAuth2 token endpoint.
- The debugger backend sends a request to the Apigee OAuth2 token endpoint (advertised by the OAuth2 wrapper API proxy).
- The API proxy makes a call to the IdP OAuth2 token endpoint to validate the authorization code and obtain an IdP-issued access token and refresh token.
- The API proxy makes a call to the IdP OIDC token endpoint to obtain user profile information for the authenticated user.
- The API proxy caches the IdP-issued refresh token for later lookup and generates an Apigee-issued access token and refresh token. These tokens are returned to the debugger backend and then to the debugger UI.
- Using the Swagger UI (or something similar) and the access token that was just obtained, an API call can be made to an API proxy that is protecting the backend API with OAuth2.
- The OAuth2-protected API proxy extracts and validates the access token (using out-of-the-box functionality).
- The API proxy removes the access token from the request and forwards the request to the backend API. In the real world, there would be some type of trust relationship established between the API gateway and the backend API (mutual Auth SSL, shared key, username and password, or something similar).
The detailed interaction between these actors is described here.
A test of the authorization code grant is shown at the end of the Apigee OAuth2 configuration post.
A description of how to use the refresh token with the debugger is available in this post.
There are many ways Apigee’s OAuth2 implementation can be used. This is just an example, but I encourage you to follow the design principles that have been laid out here. Ordinarily, the level of detail described here can be abstracted away from application developers by authentication libraries. An Apigee developer that is implementing a similar pattern will need to know these details.