The Backend for Frontend (BFF) architecture is gaining popularity for multiple reasons.
The main reason, for me as a developer, for using a BFF is that it simplifies the communication between the front-end and multiple backend services. Instead of having the frontend communicate with multiple backend services, the frontend communicates with a single BFF service that will then communicate with the backend services.
The BFF is responsible to prepare the data in the shape that the frontend expects. This can be for one backend service, or the BFF can aggregate data from multiple backend services. Doing this work in the BFF service improves the user experience because the frontend delegates this work to the BFF service, resulting in a faster experience for the user.
This sounds good, but another big reason for using a BFF is security.
Most of the frontend applications are currently using the Implicit (grant) flow to authenticate users. It's the flow where you store the access token in the browser, and where you append the token to the requests to the backend service. Sadly, as we know too well, the browser is not a secure place to store the access token. This makes it that this flow has a serious security flaw. Because the access token is exposed to the frontend application, it becomes vulnerable to access token leakage. That's why the Implicit flow is not recommended anymore. In fact, this has been the case for 3 years already.
It is not recommended to use the implicit flow (and some servers prohibit this flow entirely) due to the inherent risks of returning access tokens in an HTTP redirect without any confirmation that it has been received by the client.
Sadly, the Implicit flow is still widely used by many Single Page Applications.
Besides the security risk, many browser vendors are starting to restrict the use of third-party cookies across site boundaries because of the extra vulnerabilities they bring. This also has a big impact on the Implicit flow and breaks the current silent token renewal implementation.
So, what can we do to solve these risks? The BFF architecture solves both problems:
- it doesn't expose the access token to the frontend application;
- it doesn't rely on browser changes;
Instead of the frontend application is responsible to handle the authentication process by communicating with the Identity Provider (and storing the access token), the BFF now becomes in charge of the authentication process. By using the Authorization Code flow with Proof Key for Code Exchange (PKCE), the BFF architecture solves these security issues.
The PKCE flow is the recommended flow to prevent CSRF and authorization code injection attacks. The BFF using the PKCE flow hides the vulnerable information from the frontend application, and thus also from the attackers.
To follow along with me, you can create a (free) Auth0 account here.
The first thing we need to do is to create a new Auth0 application. Give the application a name and select the "Regular Web Applications" application type, even though in the end it's consumed by a SPA.
When the application is created, open the "Settings" tab and grab the basic information from the "Basic Information" section, which we'll use later to configure the BFF .NET API.
Lastly, scroll down until you see the "Application URIs" section.
Here, add the following URIs.
You can get the port number from the
launchSettings.json file when the .NET API is created in the next step.
For me, my Web API is running on port number
|Application Login URI||https://127.0.0.1:7012/bff/login (for some reason you need to use 127.0.0.1 instead of localhost)|
|Allowed Callback URLs||https://localhost:7012/signin-oidc|
|Allowed Logout URLs||https://localhost:7012/|
Creating a BFF API link
Next, create a new .NET WebApi project using the
While creating the project, also add the
Duende.BFF NuGet package to the project.
This package contains the necessary components to secure browser-based frontends (e.g. SPAs or Blazor WASM applications) with ASP.NET Core backends.
You could write this integration yourself, but I'm sure the Duende team got more knowledge about this topic than I am.
MyBFFApplication project in your favorite editor and add the following code to the
This setup gives us the most basic setup to secure your endpoints. Most of the implementation is using the built-in ASP.NET functionality to configure the authentication and to require that the user is authorized to access the weather forecast endpoint.
Let's take a look at what happens when we invoke a couple of endpoints.
First, let's try to access the
As expected because the weather forecast is annotated with
RequireAuthorization() and we're currently not authenticated this request results in a
401 Unauthorized response, with the corresponding logs:
To authenticate us, we need to call the
/bff/login endpoint, which is provided by the
If the .NET Web API and Auth0 are configured correctly, this endpoint redirects us to the Auth0 login page.
After logging in, we're redirected back to the API.
At this point, you should see a Cookie with the name
__MySPA (configured within the
Services.AddCookie method) in your browser.
To verify that the Cookie exists, open de Developer Tools in your browser, go to the
Application tab, and click on the cookies.
If you see a cookie with the name
__MySPA, this means that you're correctly authenticated and you're good to go.
Next, let's try to access the
/api/weatherforecast endpoint again.
To my surprise, this still results in a
401 Unauthorized response.
This time with a different cause:
After reading the logs, this makes sense.
The BFF middleware is configured to require an anti-forgery token for the requests.
By default, the BFF middleware expects the
X-CSRF header to be present in the request with a value of
To change these defaults, you can override the options within the
As we could see, when this header is not present, then the request is rejected.
When the correct anti-forgery token is present, the request is accepted and the response is returned.
To get the information about the authenticated user, there's the
In my case, this returns the following information when I'm authenticated via GitHub:
Lastly, to end the user's session use the
/bff/logout endpoint and append the session id, which is added to the user response.
In this case, this is
While the primary reason for using the BFF architecture for me was developer convenience, I recently learned (see the resources below) and realized that it also provides a more secure way of building applications. Because, in short, storing sensitive tokens in the browser is broken beyond repair.
In this blog post, we've seen how to transfer the authentication process from the SPA to the .NET Web API. In doing so, we removed the security risk of leaking tokens to potential attackers. Additionally, we made our application future-proof of changing browser implementations.
The BFF architecture provides an answer to this security flaw. Instead of storing the tokens in the browser, a cookie is used on the server to hold the user's information. A BFF also reduces the attack surface, because the BFF is the only trusted application (instead of the SPA and the API). By using a BFF we centralize the authentication and authorization process.
From my limited experience, it looks like that a BFF using the Authorization Code flow with PKCE has fewer moving parts in comparison to the Implicit flow. This makes it easier to understand, implement, deploy, and maintain.
In the next blog post, we'll consume this BFF from an Angular application.
- OAuth 2.0 Security Best Current Practice
- Microsoft Design Patterns: Backends for Frontends pattern
- Duende docs: BFF Security Framework
- JetBrains webinar with Dominick Baier: Securing SPAs and Blazor Applications using the BFF (Backend for Frontend) Pattern
- Comparing the backend for frontend (BFF) security architecture with an SPA UI using a public API by Damien Bowden (damienbod)
- From the Implicit flow to PKCE: A look at OAuth 2.0 in SPAs by Dr. Philippe De Ryck
- Auth0 Docs: Authorization Code Flow with Proof Key for Code Exchange (PKCE)
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.