In the previous blog posts in this series, we built our own Backend-for-Frontend (BFF) implementation in ASP.NET Core from scratch. Now, you might be wondering about how much effort it would take to replace our custom solution with the Duende BFF Security Framework? In this post, we’ll walk through that migration process and see just how straightforward it can be.
This is a big topic, so I’ve split it into multiple parts. You can jump to the section you need, but for background and context, it’s best to start here:
Part 1 – Introduction
Part 2 – Introducing the Backend-for-Frontend (BFF) pattern
Part 3 – Securing the Cookie Session
Part 4 – Implementing a BFF in ASP.NET Core
Part 5 – Automatic Token Renewal
Part 6 – Securing the BFF using CORS
Part 7 – Introducing the Duende BFF Library
Introducing the Duende BFF Library
The Duende BFF (Backend For Frontend) Security Framework is a comprehensive ASP.NET Core library that’s designed to secure browser-based applications like SPAs and Blazor apps by implementing the modern Backend-For-Frontend security pattern.
This library provides a robust set of features that address the common security challenges of modern web applications:
This library provides features like:
- Token extraction attack protection – keeps access tokens server-side
- Built-in CSRF attack protection through custom header requirements
- Server-side OAuth 2.0 support with complete protocol handling
- Multi-frontend support
- API reverse proxy capabilities for secure external API access
- Server-side session state management
- Back-channel logout support
- Blazor authentication state management
- And more…
Duende BFF Licensing
Before implementing Duende BFF in your project, it’s important to understand the licensing model. While the library is free for development, testing, and personal projects, production usage may require a commercial license depending on your business size and requirements. Duende Software also offers community editions and startup-friendly options.
For the most current licensing information and to determine what applies to your specific situation, visit the Duende Software website directly.
The Duende BFF Source Code
You can find all the code for this tutorial series on GitHub, with the specific implementation for this post located in the 5-Duende.BFF project folder. Having the complete working example will help you to follow along and see the migration in action.
Important: You’ll need to provide your own OIDC authentication server details to run the sample code successfully.
Adding the Duende.BFF NuGet Package
The first step in is to add the following NuGet packages:


The Duende.BFF.Yarp package is needed in order to support remote APIs.
Removing Duende.AccessTokenManagement.OpenIdConnect
Our previous implementation used the Duende.AccessTokenManagement.OpenIdConnect package for automatic token management. However, since Duende.BFF already includes this dependency; we need to remove the standalone package from our project to prevent library version conflicts.
Important: Failing to remove this package may cause your application to fail during startup due to conflicting dependencies.
Configuring Duende.BFF
Now we need to register the Duende BFF services in our Program class:
// Add Duende BFF services
builder.Services.AddBff()
.AddRemoteApis();
The AddBff() method registers the core BFF framework services (authentication, session management, local API protection), while AddRemoteApis() adds the additional services needed specifically for proxying requests to external APIs using YARP (Yet Another Reverse Proxy).
Next, we add the BFF middleware to our request pipeline. Note the specific order of these middleware components. UseBff() must be placed before UseAuthorization() because anti-forgery validation needs to happen before authorization decisions are made:
app.UseAuthentication();
app.UseBff();
app.UseAuthorization();
The app.UseBff() middleware automatically handles X-CSRF header validation. The middleware examines endpoint metadata to identify BFF-protected endpoints and validates that requests include the required X-CSRF header.
Since this protection is now handled by the framework, we can now remove our custom CSRF component:
// Remove this code
app.CheckForCsrfHeader();
The BFF Management Endpoints
In our custom BFF implementation, we created a BffController class that handled login, logout, and session information retrieval. With Duende BFF, we can remove this entire controller since the framework provides these endpoints automatically.
To enable the BFF management endpoints, add this single line to your request pipeline:
// Map BFF endpoints
app.MapBffManagementEndpoints();
This automatically exposes the following endpoints:
- /bff/login
Initiates the login process. - /bff/silent-login
- /bff/silent-login-callback
Support silent login flows. - /bff/logout
Initiates the logout process. - /bff/user
Returns information about the currently logged-in user. - /bff/backchannel
Handles OpenID Connect back-channel logout notifications. - /bff/diagnostics
Exposes diagnostics information (enabled only in development environments by default)
Mapping of Local APIs
Our existing local API in the ApiController can remain largely unchanged. We simply need to add the [BffApi] attribute to the controller class:
[ApiController]
[Route("api")]
[Authorize]
[BffApi]
public class ApiController : ControllerBase
{
…
}
The [BffApi] attribute automatically applies the security measures recommended for browser-based applications following the BFF pattern. This includes CSRF protection and proper token handling.
Mapping of Remote APIs
In our custom implementation, we handled remote API proxying through a method in the ApiController. This approach required manually proxying requests to /bff/remote while replacing the session cookie with the appropriate access token.
As you experienced firsthand, implementing this correctly involved quite a bit of complex code. Duende BFF simplifies this dramatically by leveraging Microsoft’s YARP (Yet Another Reverse Proxy) library to do this work for us.
This means we can replace our entire custom action method with this single line of code (in Program.cs):
app.MapRemoteBffApiEndpoint("/api/remote",
new Uri("https://www.secure.nu/tokenapi/gettime"))
.WithAccessToken(RequiredTokenType.User);
his approach significantly reduces the complexity and code required to implement secure remote API access, while also providing a high-performance proxy solution that would be difficult to implement ourselves.
Tweaking the frontend
We’ll make some small adjustments to the frontend by adding new buttons to the homepage that demonstrate the various BFF endpoints:

Each button serves a specific testing purpose:
- Get User
Calls the /bff/user endpoint to retrieve data about the currently logged-in user and session information. - Diagnostics
Calls the /bff/diagnostics endpoint, which returns current user details and client access token for testing purposes. - Get Identity
Calls a custom /user/GetIdentity endpoint that we’ve implemented in a custom UserController class. This endpoint returns the raw ClaimsPrincipal authentication properties, cookie details, and session cookie information as seen by ASP.NET Core controllers. Since ASP.NET Core sometimes abstracts or transforms authentication data, this endpoint helps verify your assumptions during development.
Signing Out
To implement proper logout functionality, we need to make some minor adjustments to our application. Simply calling the /bff/logout endpoint isn’t sufficient. We must include the internal session ID as a query string parameter for security purposes.
This session ID requirement acts as CSRF protection for the logout endpoint, preventing attackers from maliciously signing users out of their sessions.
The logout URL with the session ID looks like this:
/bff/logout?sid=xxxxxxxxxSessionIDxxxxxxxxxxxx
The easiest way to obtain this complete logout URL is through the /bff/user endpoint, which includes a claim named bff:logout_url that contains the properly formatted logout URL.
You can find all the implementation details for this logout flow in the site.js JavaScript file.
Testing our implementation
With all these changes in place, we can verify that our application works exactly as before. Users can sign in to the BFF application and receive an authentication session cookie, maintaining the same user experience while benefiting from the enhanced security and simplified codebase.
The Journey from DIY to Production-Ready
Migrating from our custom BFF implementation to Duende BFF proved to be remarkably straightforward. The transition required only a few minor adjustments to achieve the same functionality we had before, but with significant improvements under the hood.
The YARP integration particularly stands out, providing a much more robust and modern approach to remote API proxying compared to our prototype implementation. This upgrade gives us enterprise-grade reliability with minimal effort.
While there are many additional configuration options and advanced features we could explore, those topics will have to wait for future blog posts or webinars. The foundation we’ve built here provides a solid starting point for most applications.
Summary
Throughout this blog series, we’ve built a comprehensive BFF implementation in ASP.NET Core and secured it using industry best practices. Our goal has been to provide you with a deep understanding of how a BFF works under the hood, whether you plan to build your own solution or leverage an existing one.
Existing BFF Solutions:
- Duende.BFF
Enterprise-grade solution from the creators of Duende IdentityServer - OidcProxy.Net
A developer-friendly, open-source identity-aware reverse proxy built on YARP that requires minimal configuration to implement the BFF pattern for SPAs.
We’ve demonstrated that the BFF pattern is one of the most effective ways to harden SPA applications. It allows us to:
- Leverage the browser’s built-in security features to their fullest potential
- Keep sensitive authentication tokens and session data away from the frontend
- Implement robust CORS protection against cross-origin attacks
- Maintain a secure authentication flow without exposing credentials to JavaScript
The key insight: Understanding how a BFF works under the hood is essential for using it securely and effectively, regardless of whether you build your own or adopt an existing solution.
With the foundation we’ve built in this series, you’re now equipped to make informed decisions about BFF implementation and configuration in your own applications.
I hope you’ve found this blog post series valuable! Please share your thoughts and experiences as you implement these patterns in your own projects.
What Did We Not Cover?
What we’ve built so far is a solid starting point for a custom BFF setup. However, there are several important production considerations you may want to explore further:
- Reducing Authentication Cookie Size
To keep your authentication cookies small and secure, consider using a cookie session store. Large cookies can impact performance and hit browser size limits. See this blog post for more details: Improving ASP.NET Core Security By Putting Your Cookies On A Diet. - Logging and Monitoring
Proper logging is essential for troubleshooting and security auditing, but was not included in this walkthrough. Consider implementing structured logging for authentication events, failed requests, and security-related activities. - Data Protection Configuration
ASP.NET Core uses the Data Protection API for critical functions like cookie encryption. In production environments, you’ll want to configure this properly! I’ve covered it in the following posts: Persisting the ASP.NET Core Data Protection Key Ring in Azure Key Vault
and Introducing the Data Protection API Key Ring Debugger. - Forwarded Headers Middleware
If your application runs behind a reverse proxy or terminates HTTPS at a load balancer or gateway, you’ll need to configure forwarded headers correctly to maintain security. For more details, see my blog post:
Configuring ASP.NET Core Forwarded Headers Middleware. - Using YARP as a Reverse Proxy
In our examples, we hand-coded the proxy logic between the browser and remote APIs. For a more flexible and high-performance solution, consider using YARP (Yet Another Reverse Proxy). YARP Documentation - Content Security Policy (CSP)
With the BFF pattern, you can implement a strict Content Security Policy since the frontend is served from your backend. This provides powerful protection against XSS attacks and reduces the risk of compromised browsers executing malicious scripts. - Shared Storage for Scaling
When scaling your BFF across multiple instances, you’ll need shared storage (like Redis or SQL Server) for access tokens and session data to prevent race conditions, especially with Identity Providers that don’t allow refresh token reuse. - Authorization Policies
Authorization Policies Beyond authentication (which the BFF manages for us), you’ll need to implement proper authorization using ASP.NET Core’s policy-based authorization system to control what authenticated users can access.
Feedback, comments, or found any bugs?
Have feedback, spotted an issue, or found a typo? I’d love to hear from you! Your input helps improve the content and assists other developers who might encounter similar challenges. Get in touch!
BFF Source Code
All code for this tutorial is available on GitHub:
Security and Identity Training Workshops
Ready to take your web security skills to the next level? If you want to deepen your understanding of web application security, OpenID Connect, or authentication in .NET, explore the comprehensive workshops available.
These hands-on, practical workshops are specifically designed for developers working with real-world applications and modern identity solutions. You’ll gain actionable knowledge that you can immediately apply to secure your own projects.
About the author
Hey! I’m Tore 👋 I’m an independent consultant, coach, trainer, and a Microsoft MVP in .NET. My mission is to help developer teams solve problems more quickly and effectively across tools and platforms like ASP.NET Core, Duende IdentityServer, web security, C#, Azure, .NET, and more. Sounds helpful? I’d love to be of service! You can check out my workshops for teams and my wider consulting and coaching services here.