---
title: "BFF in ASP.NET Core #4 - Implementing a BFF from scratch"
description: "How to build a minimal Backend-for-Frontend (BFF) in ASP.NET Core from scratch: login, logout and session endpoints, OpenID Connect and a proxy for remote APIs."
url: https://nestenius.se/net/bff-in-asp-net-core-4-implementing-a-bff-from-scratch/
date: 2025-07-30
author: "Tore Nestenius"
category: ".NET"
tags: ["bff", "openid-connect"]
site: https://nestenius.se/
---

# 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](https://nestenius.se/net/implementing-bff-pattern-in-asp-net-core-for-spas/)  
[Part 2 - Introducing the Backend-for-Frontend (BFF) pattern](https://nestenius.se/net/bff-in-asp-net-core-2-the-bff-pattern-explained/)  
[Part 3 - Securing the Cookie Session](https://nestenius.se/net/bff-in-asp-net-core-3-the-bff-pattern-explained/)  
**Part 4 - Implementing a BFF in ASP.NET Core**  
[Part 5 - Automatic Token Renewal](https://nestenius.se/net/bff-in-asp-net-core-5-automatic-token-renewal/)  
[Part 6 - Securing the BFF using CORS](https://nestenius.se/net/bff-in-asp-net-core-6-securing-our-bff-with-cors/)  
[Part 7 - Introducing the Duende BFF Library](https://nestenius.se/net/bff-in-asp-net-core-7-introducing-the-duende-bff-library/)

Series overview: [BFF in ASP.NET Core, the guide to all seven parts](https://nestenius.se/bff/)

## 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:

![Architecture diagram of a BFF in ASP.NET Core showing browser, BFF endpoints, Identity Provider and downstream API](https://nestenius.se/_astro/bff-aspnet-core-architecture-browser-idp-api-endpoints.exTxpEoL_1oWJ8D.webp)

## 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:

![jQuery-based BFF demo UI showing authentication actions and a successful remote API JSON response with token forwarding](https://nestenius.se/_astro/bff-jquery-demo-authentication-api-actions-panel.DORBv6lK_ZGEsnA.webp)

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](https://github.com/tndataab/PublicBlogContent/tree/main/Backend-For-Frontend):  

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](https://tn-data.se/courses/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.

![Diagram showing BFF pattern: cookie-authenticated request hitting /api/remote endpoint, forwarded with access token to backen](https://nestenius.se/_astro/bff-cookie-to-access-token-api-proxy-diagram.rXM8CcPF_A1jDD.webp)

### 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)**](https://nestenius.se/net/bff-in-asp-net-core-5-automatic-token-renewal/) - Keep users signed in when access tokens expire.
- [**Enhanced security (Part 6)**](https://nestenius.se/net/bff-in-asp-net-core-6-securing-our-bff-with-cors/) - CORS configuration and additional protections.

Try the code and experiment with different scenarios to see the BFF pattern in action!  
To [BFF in ASP.NET Core #5 – Automatic Token Renewal](https://nestenius.se/net/bff-in-asp-net-core-5-automatic-token-renewal/).

## Related posts

- [Persisting the ASP.NET Core Data Protection Keys Ring in Azure Key Vault](https://nestenius.se/net/persisting-the-asp-net-core-data-protection-key-ring-in-azure-key-vault/)
- [Exploring what is inside the ASP.NET Core cookies](https://nestenius.se/net/exploring-what-is-inside-the-asp-net-core-cookies/)
- [Demystifying OpenID Connect’s State and Nonce Parameters in ASP.NET Core](https://nestenius.se/net/demystifying-openid-connects-state-and-nonce-parameters-in-asp-net-core/) 
- [Deploy Container to Azure App Services with System-Assigned Identity](https://nestenius.se/azure/deploy-a-container-to-azure-app-services-using-a-system-assigned-identity/)
