In this post, I would like to look at some of the advantages and disadvantages of using an implemented gateway service to process all UI API requests, optimize the business and remove some of the complexity from the user interface application.
Setup with UI using APIs directly
Modern public facing applications APIs used by UI apps are mostly protected using user delegated access tokens and the applications APIs are in the public zone because the SPA application or whatever client you use needs to request data. The API data is merged directly in the client. Each API can be secured with a separate scope and the client application would need to manage multiple access tokens. Each API would need to allow CORS for an SPA client. (You could use something like Azure Gateway as a workaround for this.) If a server rendered application was used to access the API, this is not required. With this approach, multiple APIs are exposed in the public zone.
Characteristics of this solution
UI responsible for joining API calls for different views
Multiple UIs per module, API, micro service possible
Setup with gateway implementation (BFF or API implementation)
All applications still need to implement HTTPS and the gateway should not terminate the HTTPS. All applications still require HTTPS only access and HTTP should not be used (in development as well). Then all the security headers can be applied in all services to the max.
Many micro services implementations do not optimize the API for the UI. This is not really a good idea, if using the APIs directly in a UI. By implementing a gateway service, the micro service APIs can be optimized for the UI through the gateway. This makes it possible to remove lots of unrequired or unoptimized APIs calls and reduces the amount of logic required in the UI to implement the required business.
The application gateway should not be used to apply missing security headers to new APIs or APIs created as part of your system. The security headers should be implemented where the responses are created. If using a legacy application where the security headers are missing and cannot be implemented at the source, then using the gateway in this way is good.
The application gateway could be implemented as a reverse proxy to delegate the requests onto further applications, APIs. This is good because the further APIs do not need to be exposed to the internet and the attack surface is reduced as well as the amount of APIs in the public zone. Less is more.
Characteristics of this solution
API calls can be optimized for the UI
Security can be improved by reducing the public attack surface
User authorization could be moved to the gateway service
UI and gateway could be deployed together with improved security (BFF security arch and single security client)
Gateway single point of failure
Comparing the security of the Gateway, no Gateway solutions
Improved application security with Gateway
By using a gateway, the attack surface can be reduced in the public zone. The gateway API can use a user delegated access token (or a cookie if using a server rendered application or BFF architecture) All other applications can use application tokens (OAuth client credentials flow), certificate authentication or Azure managed identities to protect the APIs. It is important that the APIs are secured and that the public user access token does not work with the private APIs.
Easier to implement systems tests for services
It is hard to implement system tests for APIs using user access tokens. These tokens can only be created using a UI test tool. The correct flow requires user interaction to authenticate. Creating a back door with some type of application access is not a good idea as this usually gets deployed in some form or other. With a gateway, it is still hard to test the gateway, but with all the private APIs, system tests can be created to test with little effort.
CORS, same domain, same site
Only the public facing gateway API needs to allow CORS if using from an SPA application. All the private APIs can completely disable this and all can use the full set of security headers. If using cookies, same site and same domain can be forced on the single gateway, UI application.
Authorization for identities with users can be reduced and most of the implementation would be rolled out in the gateway as well as been used in the user interface. The private APIs would need less complicated authorization as only the trusted application requests must be validated and not user authorization. It is not a must, that the system implements in this way, different security setups are possible like the OBO flow for the private APIs.
More choice for authorization in the private APIs.
As the private APIs are trusted APP to APP security, managed identities or certificate authentication could be used as well as OAuth client credentials. This allows for more choice but the APIs must still implement security in the application and not relay on the network security alone. I usually use the OAuth CC flow with a scope claim or role claim validation. The access tokens used for access must be validated. I never mix user access tokens and application tokens in a single API.
KISS is probably the most important way of thinking when producing software. Most solutions can be implemented as a single monolith application and the end client gets what is required with the least amount of effort. Micro services are normally not required for most solutions. Once using micro services, it is important to implement this correctly and with a solid security architecture which requires effort and planning. Do not be afraid to merge services if you see that the are tightly coupled and depend on each other. Define the public zone and private zone and where each micro service belongs. Implement the hosting environment which allows for a good application security implementation as well as a good network infrastructure. HTTP should not be used anywhere.