Writing a browser-based application is hard, and when it comes to security, the guidance changes every year. It all started with securing your Ajax calls with cookies until we learned that this is prone to CSRF attacks. Then the IETF made JS-based OAuth official by introducing the Implicit Flow; until we learned how hard it is to protect against XSS, token leakage and the threat of token exfiltration. Seems you cannot win.
In the meantime the IETF realized that Implicit Flow is an anachronism and will deprecate it. So what’s next?
See Philippe’s webinar on XSS and storing tokens in the browser for a good background on why this is not secure enough.
If you have a backend, the backend can help out the frontend with many security related tasks like protocol flow, token storage, token lifetime management, and session management among other things. With the advent of more modern security features in browsers (e.g. anti-forgery countermeasures), this is our preferred approach and we already detailed this in January 2019 here. This is also often called the BFF (Backend for Frontend) pattern.
Let’s have a closer look at all the problems the BFF pattern solves.
It’s not only your own code that must be XSS-proof. It’s also all the frameworks, libraries and NPM packages you are pulling in (as well as their dependencies). And even worse, you have to worry about other people’s code running on your domain. The recent work around Spectre attacks against browsers illustrates nicely that there is more to come.
Storing tokens on the server-side and using encrypted/signed HTTP-only cookies for session management makes that threat model considerably easier. This is not to say that this makes the application “auto-magically” secure against content injection, but forcing the attacker through a well defined interface to the backend gives you way more leverage than being able to make arbitrary API calls with a stolen token.
We wrote about this before, but in a nutshell browsers are (and will be even more in the future) restricting the usage of cookies across site boundaries to protect users from privacy invasion techniques. The problem is that legitimate OAuth & OpenID Connect protocol interactions are from a browser’s point of view indistinguishable from common tracking mechanisms.
To overcome these limitations we need the help of an application backend to bridge the gap to the authentication system, do more robust server-side token management with refresh tokens, and provide support for more future proof mechanisms like back-channel logout notifications.
On the server-side though (and especially in our case with ASP.NET Core), we have a full featured and stable OpenID Connect client library that supports all the necessary protocol mechanisms and provides an excellent extensibility model for advanced features like Mutual TLS, Proof-of-Possession, JWT secured authorization requests, and JWT-based client authentication.
Duende.BFF is Nuget package that adds all the necessary features required to solve above problems to an ASP.NET Core host. It provides services for session and token management, API endpoint protection and logout notifications to your web-based frontends like SPAs or Blazor WASM applications. Let’s have a look at the building blocks in the next section.