BFF in ASP.NET Core #4 - Implementing a BFF from scratch

BFF in ASP.NET Core #4 – Implementing a BFF from scratch

In this blog post, we’ll implement a minimal yet complete Backend-for-Frontend (BFF) in ASP.NET Core. By starting with a simple foundation and adding features incrementally, you’ll learn not just how to build a BFF, but why each component matters for securing modern web applications.

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 (coming soon)
Part 6 – Securing the BFF using CORS (coming soon)
Part 7 – Introducing the Duende BFF Library (coming soon)

The Application Architecture

To demonstrate the BFF pattern in action, we’ll build a minimal application with two main components: a jQuery-based web page for the frontend and an ASP.NET Core backend that implements the BFF logic. This simple setup lets us focus on understanding how the core BFF components work together.

The overall architecture looks like this:

BFF API Endpoints

Our BFF exposes five HTTP endpoints that demonstrate the key responsibilities of the pattern:

Authentication Endpoints

  • /bff/login
    Initiates the authentication flow by redirecting the user to the identity provider.
  • /bff/logout
    Terminates the user’s session both locally and at the identity provider.
  • /bff/session
    Returns the current user’s authentication state, including claims and tokens.
    ⚠️ Demo endpoint only – production applications should never expose tokens to the browser.

API Endpoints

  • /api/local
    A protected endpoint within the BFF that requires authentication via the session cookie.
  • /api/remote
    This proxies requests to an external API, automatically attaching the user’s access token from the server-side session.

A typical BFF supports two types of APIs:

  • Local APIs that terminate within the BFF (authenticated via the session cookie).
  • Remote APIs that are proxied to external services (where the BFF exchanges the session cookie for an access token).

From the frontend’s perspective, both types are secured using the same session cookie.

The WEB UI

Our demo interface provides a simple way to interact with all the BFF endpoints:

Each button demonstrates a core BFF capability:

  • Login
    Initiates authentication with the identity provider.
  • Get Session
    Displays the current authentication state, including claims and tokens. Note: This is for demonstration purposes only; tokens should never be exposed in production environments.
  • Logout
    Ends the session and clears authentication.
  • Call Local API
    Calls the /api/local endpoint, demonstrating cookie-based authentication for BFF-hosted APIs.
  • Call Remote API
    Calls the /api/remote endpoint, demonstrating how the BFF proxies requests with proper token handling.

The response area shows the raw API responses, making it easy to understand what data flows through each part of the system.

Getting the BFF Source Code

All code for this tutorial is available on GitHub:

The solution contains multiple projects showing the BFF evolution:

  • 1-StartCode
    Basic application with simple cookie authentication
  • 2-OpenIDConnect
    Adds real identity provider integration
  • (Additional stages covered in subsequent sections)

In the application, we have added the following two controller classes:

  • BffController
    It implements the logic for Login, LogOut, and Get Session.
  • ApiController
    This class implements the local and remote API endpoints.

Starting Point: Project 1-StartCode

When you run the first project, you’ll find a minimal setup with two key ASP.NET Core controllers:

BffController

Handles authentication operations:

  • /bff/login – Currently uses a fixed test user
  • /bff/logout – Clears the authentication cookie
  • /bff/session – Returns current user information

ApiController

Implements the API endpoints:

  • /api/local – A simple authenticated endpoint
  • /api/remote – Placeholder for external API calls

This starting point utilizes basic cookie authentication with a hardcoded user, providing a foundation to build upon before adding OpenID Connect integration.

You can run 1-StartCode immediately to see basic BFF functionality in action. The local API endpoints work, but you’ll notice two limitations:

  • No real authentication or OpenID-Connect support (just a fixed test user).
  • The remote API call fails (it needs an access token we don’t have yet).

Adding OpenID Connect support

Now let’s upgrade our basic BFF to use real authentication. This transformation requires five specific changes:

  1. Add NuGet packages
    Install the OpenID Connect authentication middleware.
  2. Configure OpenID Connect
    Set up the authentication handler in Program.cs.
  3. Update the challenge scheme
    Change from “Cookies” to “oidc”.
  4. Remove the fixed user logic
    Delete the SignInUser method from BffController.
  5. Update logout behavior
    Ensure it signs out from both the BFF and the identity provider.

Important: You’ll need to update the OpenID Connect settings with your own identity provider configuration, including client registration details and endpoints.

After these changes (available in the 2-OpenIDConnect project), your BFF now:

  1. Authenticates users through a real identity provider
  2. Stores tokens securely in server-side session
  3. Provides cookie-based authentication for the frontend
  4. Proxies remote API calls with proper access tokens
  5. Handles complete logout flow (local + identity provider)

This is a fully functional BFF implementation with all the core security features in place.

Want to dive deeper into OpenID Connect and OAuth?

If you’re interested in mastering these technologies, I also teach a hands-on workshop: Introduction to OpenID Connect and OAuth. It’s a great way to build a solid understanding and get practical experience with real-world scenarios.

Supporting Remote APIs

One of the most valuable features of the BFF pattern is its secure handling of external API calls. Unlike traditional SPAs, which must manage tokens in JavaScript, the BFF acts as a secure reverse proxy, keeping sensitive credentials out of the browser.

Here’s the complete flow when calling an external API:

  • Frontend → BFF:
    The browser sends a request to /api/remote with the session cookie.
  • Cookie → Token:
    The BFF validates the cookie and retrieves the stored access token.
  • BFF → API:
    The BFF forwards the request to the external API with the access token.
  • API → Frontend:
    The response flows back through the BFF to the browser.

Why This Matters

This design achieves three critical goals:

  • Security: Browser cookies never leave your domain.
  • Simplicity: The frontend only deals with one authentication mechanism.
  • Control: All token management happens on server-side.

The remote API only recognizes valid access tokens, whereas the browser only recognizes session cookies. This clean separation of concerns is what makes the BFF pattern so effective for modern web applications.

Won’t This Add Latency?

A common concern is whether adding a proxy layer impacts performance. In practice, the overhead is minimal because:

  • Connection reuse:
    With HTTP/2, the browser maintains a single multiplexed connection to your BFF, which is often faster than opening multiple connections to different APIs.
  • Negligible overhead:
    Most API calls already traverse multiple layers (load balancers, API gateways, reverse proxies). One more hop rarely makes a difference. Often, one or more database requests are involved, and these take significantly more time than a single network hop.
  • Caching opportunities:
    The BFF can implement caching for frequently accessed data.

The security benefits far outweigh any minimal latency:

  • API abstraction:
    Internal service architecture stays hidden from external clients.
  • Centralized security:
    Rate limiting, authentication, and monitoring all happen in one place.
  • Attack surface reduction:
    Only your BFF needs to be hardened against internet-facing threats.
  • Token isolation:
    Compromised browser code can never access your API tokens.

What’s next?

We now have a working BFF with all the core features. To make it production-ready, we’ll need:

  • Automatic token renewal (Part 5) – Keep users signed in when access tokens expire.
  • Enhanced security (Part 6) – CORS configuration and additional protections.

Try the code and experiment with different scenarios to see the BFF pattern in action!
(Coming soon)

 

Related posts by me:

Share This Story

About The Author

Hi, I’m Tore! I have been fascinated by computers since I unpacked my first Commodore VIC-20. I am a Microsoft MVP in .NET and  I provide freelance application development, developer training and coaching services, focusing on ASP.NET Core, IdentityServer, OpenID Connect, Architecture, and Web Security. Let’s connect on LinkedIn and Twitter!

Related Posts

Do You Want Tore To Be Your Mentor?

Services 🚀

I offer training and coaching for professional developers and consulting services for startups and enterprises. Find out more on my business website. 

Tore’s Newsletter

Be the First to Know! Get notified about my latest blog posts, upcoming presentations, webinars, and more — subscribe today!

Cartoon of Tore Nestenius

About me

Hi! I’m Tore Nestenius. I’m a trainer and senior software developer focusing on Architecture, Security & Identity, .NET, C#, Backend, the Cloud, and more.

Do You Want Tore To Be Your Mentor?

Services 🚀

I offer training and coaching for professional developers and consulting services for startups and enterprises. Find out more on my business website. 

Blog Categories