Can we get rid of Backend For Frontend when using an API Gateway?

Introduction

12 weeks ago, a fellow student and I started an internship with the topic "Cookie to Token converter on the Apigee Gateway". We are two students from the Thomas More University in Mechelen (Belgium). We are passionate about the world of Cybersecurity and therefore found this an interesting internship.

What is our project about?

The main goal of this project is to build a prototype framework on the Apigee Gateway that converts a cookie into an access token and also manages it without a Backend For Frontend. In modern web applications, clients can directly invoke APIs, interact with the authorization server and manage access tokens themselves.

cookie2token.png

However, there are strong opinions (based on a statement of the book “API Security in Action (2020)”, Neil Madden) that web applications should continue to use cookies and that only a server should interact with the authorization server, these opinions exist because a simple web client cannot keep secrets and therefore is not as safe to interact with the as authorization server.

Nowadays, if you want to use this method to secure APIs, you need something that can convert the cookie received from the client into an access token so that the server component (in this case the API) can understand it. For this, a Backend For Frontend is used, the BFF interacts with the authorization server and so on.  

What is a Backend For Frontend actually?

Some of you may be wondering what exactly is a Backend For Frontend? Well, a BFF is a design pattern created not only with the developer in mind, but also with the user and their experience in mind. The role of such a Backend For Frontend is quite simple, namely it is a kind of interface between the frontend of an application and the microservices in the backend. Also, a BFF is focused on a single frontend, this ensures that the frontend can remain simple and that data can be seen in a uniform way by the different backends.

Why remove a Backend For Frontend?

By doing research about this topic we couldn’t really find a lot of pro’s and con’s about why you should use a Backend For Frontend or why you shouldn’t, this is why we formulated our own opinion about removing the BFF from the API Security when using an API Gateway.

We think that using a Backend For Frontend is not really necessary for converting the cookie to an access token, as we can achieve the same thing by using the Apigee Gateway. An API management tool like Apigee can manage our “cookies” and the access tokens or ID tokens received from the external Identity Provider.

How do we let Apigee manage our cookies?

Well, because Apigee doesn’t really support cookies out of the box, we have to trick Apigee into using the internal identity provider to create us a so-called access token that we then can use as our cookie for session management. When using the “cookie” we can later add some custom attributes to it, in order to manage the external access token. The custom attributes we are adding to this “cookie” are the external access token, the ID token if provided, the tokens expire time, the refresh token, the API key of the Apigee app and a Cross Site Request Forgery token (CSRF).

How is our Apigee Proxy build?

Our Apigee proxy has currently four endpoints /app, /auth, /callback & /userinfo. The first thing you need to do to use this project is starting your app via the /app endpoint (currently a demo app), this endpoint will only load the HTML through Apigee from some (cloud) storage, the other files (css, images, *.js) can be directly loaded by the client without going via Apigee. By using this method, the app will run as a First Party application which eliminates the most CORS policy problems, and we can (more) easily implement CSRF protection with CSRF cookie.

/app endpoint:

The URL will be something like: https://yourdomain.com/app/yourclientid
Possible future improvement: return cookie already here instead of /auth endpoint.

YorickCleerbout_0-1652791768969.png

/auth endpoint:

The URL will be something like:  https://yourdomain.com/auth?key=yourclientid
When letting your app redirect to this endpoint, it will create the authorization cookie and initialize the OAuth2 sequence with the registered external identity provider.

YorickCleerbout_1-1652791878140.png

Looking back at the design, a future version would better return the cookie in the /app and so the API key would not be necessary anymore.

/callback endpoint:

This endpoint will be used by the external IDP as callback uri, the authorization code will be returned by the IDP to this endpoint. Along with this redirect, the browser will also return the cookie.  Next we check if the cookie is known and derive the client app and the credentials to authenticate against the IDP. Next, we obtain the access token from the /token endpoint of the Identity Provider and add all the information, most importantly the backend access token, to the local Apigee access token used as cookie.

YorickCleerbout_2-1652791913246.png

/userinfo endpoint:

The URL will be something like: https://yourdomain.com/userinfo (This endpoint must be used as an API call)

Finally, we have an endpoint just for testing purposes which validates your cookie and the CSRF-Token as well as refresh your current access token. In addition, we also added some functionality to this endpoint what will extract the information located in the cookie attributes in order to read the user information stored in the ID Token you receive when requesting the access token with the scope OpenID.

YorickCleerbout_3-1652791957592.png

How does our project work?

YorickCleerbout_4-1652792043091.png

Steps on how our project works:

  1. The client application is started by going to the URI https://yourdomain.com/app/yourclientid 
  2. Apigee looks up the client ID, determines where .html page of the app is located and returns the web page.
  3. Next, the client points itself to the /auth endpoint within our Apigee proxy and passes the API key as parameter.
  4. The API key is verified within Apigee: if valid, an access token is generated with the internal OAuth provider of Apigee. This internal, Apigee access token will be (ab)used as “cookie” from now on.
  5. After the “cookie” is created successfully, we set the cookie in the client browser and redirect the client to the identity provider. The identity provider to use is derived from an attribute of the client app in Apigee.
  6. After a successful login by the user at the IDP, the IDP will redirect the browser (client) back to our /callback endpoint with an authorization code. An authorization code which has a very limited lifetime.
  7. On the /callback endpoint, we extract the Authorization Code from the URL and call the /token endpoint of the IDP to exchange authorization code into an access token and (OpenId) ID token.
  8. Finally, we attach the access token, ID token and other information to the cookie (or rather the internal Apigee token).

Conclusion

After experimenting with Apigee, cookies, OAuth2, etc. and building our prototype, we proofed that the session management part can be handled just as well by an API Gateway than by a Backend For Frontend. Because this is just a prototype, a variety of things can be improved for the future, things like the API key that must be given as parameter or the cookie that is created before authentication instead of when the application launches.

As we had only twelve weeks to work on this, there are a lot of things that can still be implemented for the future. Unfortunately, we did not have the time to work on these features, for example isolating  the code nicely in shared flows.

We have also created a GitHub repository for this project, you can check it out using this link: https://github.com/I8C/apigee-cookie-to-token 

7 3 2,616
3 REPLIES 3

guycrets
Participant IV

Yorick, congrats, nice work!

This is an interesting pattern, and thank you for writing it up.  One challenge I see with this approach is that API calls handled by Apigee are expensive.  Much more expensive than requests handled by a web server, like nginx. I supposed based on that cost, this would not be a practical approach for broad adoption. 

Does anyone have any ideas or suggestions on a set of decision criteria, for choosing something like this, versus implementing a BFF and hosting it on cloud run or similar ?  How does call volume fit in to the decision? What other criteria apply?  How would I decide?

Only the initial request for the .html request is going through the API GW, to make life easier with the application as 1st party client. Securing API calls is what Apigee is made, bought and implemented for. If a BFF sits in front of the API GW, the call will be forwarded and secured anyway by the API GW. So can’t really follow your reasoning about being too expensive. Looking forward to your answer!

PS: really had fun learning and working with Apigee as a student!