---
title: "Backend For Frontend Authentication Pattern with Auth0 and ASP.NET Core"
description: "Learn how to implement the Backend For Frontend (BFF) pattern using Auth0 and ASP.NET Core to eliminate token exposure and enhance SPA security."
authors:
  - name: "Andrea Chiarelli"
    url: "https://auth0.com/blog/authors/andrea-chiarelli/"
date: "Apr 29, 2026"
category: "Developers,Tutorial,.NET"
tags: ["dotnet", "bff", "oauth", "backend-for-frontend", "design-pattern"]
url: "https://auth0.com/blog/backend-for-frontend-pattern-with-auth0-and-dotnet/"
---

# Backend For Frontend Authentication Pattern with Auth0 and ASP.NET Core

Single-Page Applications (SPAs) have a token problem. Store access tokens in `localStorage`, and you're one XSS vulnerability away from a breach. Store them in memory, and they vanish on refresh. The Backend For Frontend (BFF) pattern solves this by keeping tokens where they belong: on the server.

In this guide, you'll implement BFF authentication using React and ASP.NET Core with Auth0. By the end, your SPA will authenticate users and call remote APIs without ever touching an access token. The backend handles all of it, and the browser only sees a secure, `HttpOnly` cookie.

## What Is the Backend For Frontend Authentication Pattern?

The Backend For Frontend (BFF) authentication pattern is a powerful architectural pattern that allows to improve the security of Single-Page Applications (SPAs) using OAuth and OpenID Connect.

The choice of an appropriate OAuth flow is heavily influenced by the client type, as defined in [section \#2.1 of the OAuth 2.0 specification](https://datatracker.ietf.org/doc/html/rfc6749). The specification distinguishes between two types: public and confidential.

**Public clients** are those where secrets cannot be reliably protected, such as SPAs running in a browser or native applications on user devices (like mobile phones or smart TVs). Exposure of secrets could occur through source code inspection or decompilation of binaries.

In contrast, **confidential clients** have the ability to securely store secrets in a private location. A common example is a web application running on a web server, which can store secrets on the server.

The client type of an application is crucial as it determines the suitable OAuth flows that minimize the authentication and authorization risks for the application.

To address the security challenges of managing access tokens for public clients running in a browser, the [Backend For Frontend (BFF) pattern](https://auth0.com/blog/the-backend-for-frontend-pattern-bff/) for authentication was developed. This pattern needs a dedicated backend component to handle all the authentication and authorization ceremonies and manage access and refresh tokens. The BFF pattern operates on top of OAuth, leveraging [OpenID Connect](https://openid.net/connect/) to request and receive identity information about authenticated users.

Check out the [Backend For Frontend (BFF) pattern](https://auth0.com/blog/the-backend-for-frontend-pattern-bff/) article to learn more details. For your quick reference, consider the following diagram describing how the BFF pattern works:

![Sequence diagram of the Backend for Frontend (BFF) authentication flow using an authorization server and an external API.](https://images.ctfassets.net/23aumh6u8s0i/6uM2x48XekNyFpODMiFjJq/c351e6278219b3865bd0bc6dd33e828b/backend-for-frontend-bff-flow.png)

Let’s quickly go through the steps of the flow shown above:

1. When the frontend application needs to authenticate the user, it calls a backend API endpoint (e.g., `/login`) to initiate the login handshake.  
2. The backend starts the usual OpenID Connect negotiation with the authorization server: it redirects the user browser to the authorization endpoint to authenticate the user and get the needed tokens.  
3. The backend receives the ID, access, and refresh tokens and stores them in a cache.  
4. A cookie is issued for the frontend that represents the user’s authenticated session. This cookie is marked as `HttpOnly`, `Secure`, and `SameSite=Strict` to maximize frontend security.  
5. When the frontend needs to call an external API, it sends a request to the backend including the cookie.  
6. The backend retrieves the access token from the cache and makes a call to the external API, including that token in the authorization header.  
7. The external API returns a response  
8. The backend forwards the response to the frontend.

Be aware that this pattern does not work for a pure SPA that relies on calling external APIs directly from JavaScript or a serverless backend (e.g., AWS Lambda or Azure Functions).

## Backend For Frontend in ASP.NET Core

Visual Studio ships with several templates for SPAs with an ASP.NET Core backend. Those templates allow you to select frontends in Vue, Angular, and React. For this article, we will use the template ASP.NET Core SPA template that uses React and JavaScript, as shown in the following screenshot:

![Selecting the React and ASP.NET Core project template in Visual Studio](https://images.ctfassets.net/23aumh6u8s0i/1fLKOHcPyckSXxiMdjsEoo/ccf0dc067aaf22c2016318f464fd9036/visual-studio-react-aspnet-core-template.png)

In case you are not using Visual Studio, you can download the initial project by running the following command in a terminal window:

```shell
git clone --branch starting-point --single-branch https://github.com/auth0-blog/bff-auth0-dotnet.git
```

### The structure of the project

The projects created with that template from Visual Studio will have the following folder structure:

* `Backendforfrontend.client`. This folder contains a sample SPA implemented with React. This is the app that we will modify to support the BFF pattern.  
* `BackendForFrontend.Server`. This folder contains the API implemented with ASP.NET Core and consumed from the SPA. In other words, it's the backend.

Throughout the article, we will implement the BFF pattern by applying the following changes:

* Add Auth0 authentication to the ASP.NET Core backend.  
* Move the API implementation from the ASP.NET Core backend (BFF) to a new ASP.NET Core minimal API (the remote API).  
* Add authentication support to the React application.  
* Add support for calling the remote API through the BFF.

Once you have your starting React application with ASP.NET Core backend, run it from Visual Studio or through `dotnet run` in a terminal window and take note of the URLs of the two applications. In my case, my React app runs at `https://localhost:54575` while the backend runs at `https://localhost:7125`.

## Auth0 Configuration

Before modifying any code, we will first configure our application in Auth0. That configuration will give us access to the settings for integrating OpenID Connect authentication.

To start, you need to access your [Auth0 Dashboard](https://manage.auth0.com/). If you don't have an Auth0 account, you can [sign up for a free one](https://a0.to/blog_signup) right now\!

### Create an application in the Auth0 Dashboard

The first thing we will do is to create a new application in the Auth0 Dashboard. An Auth0 application serves as an entry point for obtaining the keys and endpoints we need in our web application. Go to your dashboard, click the [*Applications*](https://manage.auth0.com/#/applications) menu on the left, and then *Create Application*.

The *Create Application* button will show a popup window to define the configuration of our application:

![The Auth0 dashboard' screen to register a web application.](https://images.ctfassets.net/23aumh6u8s0i/7LudqFxMXwxrVPkkr5hutI/ba1e5c02409b885060f4d1263ddf107a/create-regular-web-app-auth0-dashboard.png)

Pick a name for your web application, and select the option *Regular Web Application*. Do not confuse your application with a Single Page Web Application. Even if we are going to implement a SPA with React, we will rely on the ASP.NET Core backend to negotiate the tokens. When choosing *Regular Web Application*, we are telling Auth0 that our application will use the [Authorization Code Flow](https://auth0.com/docs/flows/authorization-code-flow), which requires a backend channel to receive the ID and access tokens, and that is exactly what we need to get that magic happening in our ASP.NET Core backend.

Once the application is created, go to the *Settings* tab and take note of the following settings:

* Domain  
* Client ID  
* Client Secret

Those are the ones you will need to configure OpenID Connect in the web application.

### Configure the Callback URL

The next thing is to configure the Callback URL for our web application. This is the URL where Auth0 will post the authorization code after the user authentication. This URL can be added in the Allowed URLs field for our application. For our sample, we will use `https://localhost:54575/callback`. Use the port of your specific React application instance.

### Configure the Logout URL

The logout URL is where Auth0 will redirect the user after the logout process has been completed. It must be added to the *Allowed Logout URLs* field under the application settings, or Auth0 will return an error otherwise when the user tries to do a logout. For our sample, we will use `https://localhost:54575`.

### Create an API in the Auth0 Dashboard

We also need to create an Auth0 API in the Auth0 Dashboard. So, go to the [APIs section](https://manage.auth0.com/#/apis) and click on *Create API*, as shown in the following picture:

![Auth0 dashboard's screen to register an API.](https://images.ctfassets.net/23aumh6u8s0i/5liH4toFlrpd6f5l7yh1wa/a91fbf68c0699e20ed2073dfa91dd721/create-api-auth0-dashboard.png)

This will open a new window for configuring the API. Configure the following fields under the settings tab in that window.

* **Name**, a friendly name or description for the API. Enter “Weather Forecast API” for this sample.  
* **Identifier** or Audience, which is an identifier that the client application uses to request access tokens for the API. Enter the string `https://weatherforecast`.

> For simplicity, this article will not discuss scope management associated with access tokens. Keep in mind that in a production scenario, it's good practice to manage scopes according to the [principle of least privilege](https://auth0.com/blog/oauth2-access-tokens-and-principle-of-least-privilege/).

Finally, click on the *Save* button to save the changes. At this point, our API is ready to be used from the ASP.NET Core application.

## Configuring the ASP.NET Core Application

To enable our application to use OpenID Connect via Auth0, we will use the [Auth0 ASP.NET Core Authentication SDK](https://github.com/auth0/auth0-aspnetcore-authentication).

Open the Package Manager Console for NuGet in Visual Studio and run the following command:

```shell
Install-Package Auth0.AspNetCore.Authentication
```

Once the Nuget package is installed in our project, we can go ahead and configure the middleware in the `Program.cs` file under the root folder of the ASP.NET Core project.

Let’s start by adding the following `using` statements at the top of the `Program.cs` file:

```c#
// BackendForFrontend.Server/Program.cs

using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using System.Net.Http.Headers;
using System.Security.Claims;

//...existing code...
```

Then add the statements highlighted below:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

var builder = WebApplication.CreateBuilder(args);

//👇new code
builder.Services.AddAuth0WebAppAuthentication(options =>
{
  options.Domain = builder.Configuration["Auth0:Domain"];
  options.ClientId = builder.Configuration["Auth0:ClientId"];
  options.ClientSecret = builder.Configuration["Auth0:ClientSecret"];
})
  .WithAccessToken(options =>
  {
    options.Audience = builder.Configuration["Auth0:ApiAudience"];
  });

builder.Services.AddAuthorization();
//👆 new code

builder.Services.AddOpenApi();

//...existing code...
```

The `AddAuth0WebAppAuthentication()` extension method sets up the OIDC flow and cookie-based session.

The `WithAccessToken()` method tells Auth0 to include an access token for the specified audience in the session. The BFF stores this token server-side; the browser never sees it.

To complete the configuration, update the existing `appSettings.json` file by including the settings we got from the Auth0 Dashboard before, as shown below:

```json
// BackendForFrontend.Server/appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  //👇new settings
  "Auth0": {
    "Domain": "YOUR_DOMAIN",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET",
    "ApiAudience": "YOUR_API_AUDIENCE"
  }
  //👆new settings
}
```

Replace the placeholders `YOUR_DOMAIN`, `YOUR_CLIENT_ID`, `YOUR_CLIENT_SECRET` with the values from registering your Regular Web App with Auth0.

Replace the `YOUR_API_AUDIENCE` with the identifier of your Auth0 API.

### Add the endpoints for handling authentication

We will add three new endpoints to our ASP.NET Core application to manage authentication-related tasks:

* `/bff/login` for initiating the OpenID Connect login handshake with Auth0.  
* `/bff/logout` for logging out from the web application and also from Auth0.  
* `/bff/getUser` for getting data about the authenticated user in the current session. This is an API that the React application will invoke to get the authentication context for the user.

Add the following code to the `Program.cs` file of your ASP.NET Core application:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

app.UseHttpsRedirection();

//👇new code
app.MapGet("/bff/login", async (HttpContext httpContext, string returnUrl = "/") =>
{
  var authenticationProperties = new LoginAuthenticationPropertiesBuilder()
          .WithRedirectUri(returnUrl)
          .Build();

  await httpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
});
//👆new code

//...existing code...
```

This endpoint initiates the OIDC authorization code flow by issuing an HTTP 302 redirect to Auth0. `ChallengeAsync()` triggers the middleware to build the [Auth0 login URL with all the required parameters](https://auth0.com/blog/anatomy-of-an-oauth2-authorization-request/).

Just below the `/bff/login` endpoint implementation, add the code highlighted below:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

//👇new code
app.MapGet("/bff/logout", async (HttpContext httpContext) =>
{
  var authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
          .WithRedirectUri("/")
          .Build();

  await httpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
  await httpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}).RequireAuthorization();
//👆new code

//...existing code...
```

This endpoint implements the logout. Two `SignOutAsync()` calls are needed here: the first notifies Auth0 (server-side logout), the second clears the local cookie session. `.RequireAuthorization()` prevents unauthenticated users from hitting this endpoint.

Implement the `/bff/getuser` endpoint below the logout endpoint as follows:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

//👇new code
app.MapGet("/bff/getUser", (HttpContext httpcontext) =>
{
  var result = new { isAuthenticated = false, claims = new[] { new { type = "", value = "" } } };

  if (httpcontext.User.Identity?.IsAuthenticated == true)
  {
    var claims = ((ClaimsIdentity)httpcontext.User.Identity!).Claims
      .Select(c => new { type = c.Type, value = c.Value })
      .ToArray();

    result = new { isAuthenticated = true, claims };
  }

  return (object)result;
});
//👆new code

//...existing code...
```

This endpoint is intentionally **not** protected. The React app calls it on load to check whether the user is already logged in. If not authenticated, it returns `{ isAuthenticated: false }`. When authenticated, it returns the user claims (name, email, etc.). The frontend app never gets the ID token.

Now let's pause the backend changes for a moment to implement the remote API that will be indirectly called by the React application.

## Creating the Remote API

As you may guess, the remote API will be the classic Web API that returns the weather forecast data. You have two options to create this API:

* Create it using the standard Minimal API template in Visual Studio or the .NET CLI and then [secure it with Auth0 following the instructions in this blog post](https://auth0.com/blog/securing-aspnet-minimal-webapis-with-auth0/).  
* Create it using the Minimal API template from the [Auth0 Templates for .NET](https://github.com/auth0/auth0-dotnet-templates). You will get the API with Auth0 embedded.

To keep things easy to implement, in this article we will use the Auth0 Templates for .NET.

### Create the ASP.NET Core API in Visual Studio

If you don’t have the [Auth0 Templates for .NET package](https://www.nuget.org/packages/Auth0.Templates) installed in your machine, install it with the following command:

```shell
dotnet new install Auth0.Templates
```

Make sure to restart Visual Studio to see the new templates.

In Visual Studio, select the Auth0 ASP.NET Core Web API template, as shown in the following screenshot:

![Select the Auth0 ASP.NET Core Web API template from Visual Studio.](https://images.ctfassets.net/23aumh6u8s0i/1qnXk3EN5t36XGdYp3cjaz/f439eda740e50c57d1bc2fcb5aee249e/auth0-aspnet-core-web-api-template-selection.png)

Continue with the guided tool and provide the requested info, including the Auth0 settings for configuring the API, as shown below:

![Configuration form for the Auth0 ASP.NET Core Web API template.](https://images.ctfassets.net/23aumh6u8s0i/7cVJgeyNqy6V4s1LNu6BGF/59976bd18608a2eaac50a38b9d3afb52/settings-for-auth0-template-aspnet-core-web-api.png)

Insert the Auth0 domain and the API identifier you registered in the Auth0 dashboard earlier.

Once you create the project, you will have a working API using Auth0 for access control.

Take note of the URL of the newly created minimal API. In my case it’s `https://localhost:7158`. You will need it soon.

## Mapping the API Call

Now we have the SPA implemented in React, the ASP.NET Core BFF, and the remote ASP.NET Core API.

We will convert the `weatherforecast` endpoint in our BFF to act as a reverse proxy and call the equivalent API hosted remotely on a different site.

### Prepare for the remote call

First, let’s add a new key to the BFF project’s `appsettings.json` file, as shown below:

```json
// BackendForFrontend.Server/appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "Auth0": {
    "Domain": "YOUR_DOMAIN",
    "ClientId": "YOUR_CLIENT_ID",
    "ClientSecret": "YOUR_CLIENT_SECRET",
    "ApiAudience": "YOUR_API_AUDIENCE"
  },
  //👇new key
  "WeatherApiBaseUrl": "YOUR_REMOTE_API_URL"
}
```

The `WeatherApiBaseUrl` key represents the base URL of the remote API you just created.

In the `Program.cs` file, add the following code:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

builder.Services.AddAuthorization();

//👇new code
builder.Services.AddHttpClient("WeatherApi", client =>
{
  client.BaseAddress = new Uri(builder.Configuration["WeatherApiBaseUrl"]
    ?? throw new InvalidOperationException("WeatherApiBaseUrl is not configured"));
});
//👆new code

//...existing code...
```

`AddHttpClient("WeatherApi", ...)` registers a named `HttpClient` with the resource API's base address. Using `IHttpClientFactory` (instead of `new HttpClient()`) is best practice: it manages connection pooling and lifetime automatically.

### Call the remote API

The remote API will require an access token, so the endpoint implementation will first get it and then send a request with the token in the `Authorization` header. Replace the current implementation of the `weatherforecast` endpoint with the following code:

```c#
// BackendForFrontend.Server/Program.cs

//...existing code...

//👇changed code
app.MapGet("/bff/weatherforecast", async Task<IResult> (
  HttpContext httpContext,
  IHttpClientFactory httpClientFactory) =>
{
  var httpClient = httpClientFactory.CreateClient("WeatherApi");

  var accessToken = await httpContext.GetTokenAsync(Auth0Constants.AuthenticationScheme, "access_token");

  if (string.IsNullOrEmpty(accessToken))
    return Results.Unauthorized();

  var request = new HttpRequestMessage(HttpMethod.Get, "WeatherForecast");
  request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

  var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);

  if (!response.IsSuccessStatusCode)
  {
    response.Dispose();
    return Results.StatusCode((int)response.StatusCode);
  }

  var contentType = response.Content.Headers.ContentType?.ToString() ?? "application/json";
  return Results.Stream(await response.Content.ReadAsStreamAsync(), contentType);
})
.RequireAuthorization();
//👆changed code

//...existing code...
```

This is the BFF's core proxy endpoint. It:

* Retrieves the access token from the server-side session (the browser never has it).  
* Attaches it as a `Bearer` header on the forwarded request to remote API.  
* Streams the upstream response directly back to the client for efficiency.  
* Is protected with `.RequireAuthorization()`.

## Securing the React Application

So far, we have added all the plumbing code on the backend to enable authentication with Auth0 using OpenID Connect. The backend handles user authentication and sends an authentication cookie to the React app. We also added a `getUser` endpoint that can be used to determine whether the user is authenticated and get basic identity information about them. Let's now see the needed changes for the React client application.

### Configure the Vite proxy

The React application uses Vite as its build tool. The tool configuration includes a proxy that enables calls to the ASP.NET Core backend. The current configuration is as follows:

```json
//backendforfrontend.client/vite.config.js

//...existing settings...

    server: {
        proxy: {
            '^/weatherforecast': {
                target,
                secure: false
            }
        },

//...existing settings...
```

You need to replace that single rule with two new ones: one for all `/bff` paths and one for the Auth0 `/callback` path, as shown below:

```json
//backendforfrontend.client/vite.config.js

//...existing settings...

    server: {
        proxy: {
        //👇changed code
            '^/bff': {
                target,
                secure: false
          },
            '^/callback': {
            target,
            secure: false
          }
        //👆changed code
        },

//...existing settings...
```

The `/callback` proxy rule is critical. After the user authenticates at Auth0, Auth0 redirects the browser to `https://localhost:54575/callback`. However, the ASP.NET Core backend runs on a different port (`7125`), so without this proxy rule the browser would land on a port with no handler.

### React context for authentication

To provide the authentication state to the entire React tree, we will create a global context, keeping the authentication logic in one place.

Move to the folder `backendforfrontend.client/src` and create an `AuthContext.jsx` file with the following code:

```javascript
//backendforfrontend.client/src/AuthContext.jsx

import React, { useState, useEffect, useContext } from "react";

export const AuthContext = React.createContext();
export const useAuth = () => useContext(AuthContext);
export const AuthProvider = ({
    children
}) => {
    const [isAuthenticated, setIsAuthenticated] = useState();
    const [user, setUser] = useState();
    const [isLoading, setIsLoading] = useState(false);

    const getUser = async () => {
        const response = await fetch('/bff/getUser');
        const json = await response.json();

        setIsAuthenticated(json.isAuthenticated);
        setIsLoading(false);
        if (json.isAuthenticated) setUser(json.claims);
    }

    useEffect(() => {
        getUser();
    }, []);

    const login = () => {
        window.location.href = '/bff/login';
    }

    const logout = () => {
        window.location.href = '/bff/logout';
    }

    return (
        <AuthContext.Provider
            value={{
                isAuthenticated,
                user,
                isLoading,
                login,
                logout
            }}
        >
            {children}
        </AuthContext.Provider>
    );
};
```

On mount, `getUser()` calls the `/bff/getUser` endpoint to restore the session state (e.g. if the user refreshes the page after logging in).

`login()` and `logout()` are full-page navigations (`window.location.href`), not `fetch` calls. In fact, the login flow requires browser redirects, not XHR.

To enable the authentication context on the React application, edit the `main.jsx` file as shown below:

```javascript
//backendforfrontend.client/src/main.jsx

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
//👇new code
import { AuthProvider } from "./AuthContext"
//👆new code

createRoot(document.getElementById('root')).render(
  <StrictMode>
    //👇updated code
    <AuthProvider>
        <App />
    </AuthProvider>
    //👆updated code
  </StrictMode>,
)
```

### Create the login and logout components

Create a file named `LoginButton.jsx` in the `src` folder and add the following code:

```javascript
//backendforfrontend.client/src/LoginButton.jsx

import { useAuth } from './AuthContext';

const LoginButton = () => {
  const { login } = useAuth();
  return (
    <button
      onClick={() => login()}
      className="button login"
    >
      Log In
    </button>
  );
};

export default LoginButton;

```

Similarly, to implement the logout button, add a new file `LogoutButton.jsx` to the same folder with the following code:

```javascript
//backendforfrontend.client/src/LogoutButton.jsx

import { useAuth } from './AuthContext';

const LogoutButton = () => {
  const { logout } = useAuth();
  return (
    <button
      onClick={() => logout()}
      className="button logout"
    >
      Log Out
    </button>
  );
};

export default LogoutButton;
```

### Add the profile component

Let’s add the profile component that will read the claims array returned by the `/bff/getUser` endpoint and extracts the user's name:

```javascript
//backendforfrontend.client/src/Profile.jsx

import { useAuth } from './AuthContext';

const Profile = () => {
  const { isAuthenticated, user } = useAuth();

  const getUserName = function (claims) {
    const nameClaim = claims.find(claim => claim.type === 'name');
    return nameClaim ? nameClaim.value : 'User';
  };

  return (
    isAuthenticated && user ? (
      <div>
        <h2>Welcome, {getUserName(user)}!</h2>
      </div>
    ) : null
  );
};

export default Profile;
```

### Update the UI

Now, let’s combine the components we have created and make the final adjustments.  
First, open the `App.jsx` file and add the following imports after the existing ones:

```javascript
//backendforfrontend.client/src/App.jsx

//...existing code...

import { useAuth } from './AuthContext';
import LoginButton from './LoginButton';
import LogoutButton from './LogoutButton';
import Profile from './Profile';

//...existing code...
```

Read the authentication state inside the `App` component, as shown below:

```javascript
//backendforfrontend.client/src/App.jsx

//...existing code...

function App() {
    const [forecasts, setForecasts] = useState();

    //👇new code
    const { isAuthenticated, login, logout } = useAuth();

    //...existing code...
}

export default App;
```

Now, modify the call to the `weatherforecast` endpoint as highlighted here:

```javascript
//backendforfrontend.client/src/App.jsx

//...existing code...

    async function populateWeatherData() {
        //👇old code
        // const response = await fetch('weatherforecast');
        const response = await fetch('/bff/weatherforecast');
        //👆changed code

        if (response.ok) {
            const data = await response.json();
            setForecasts(data);
        }

//...existing code...
```

Finally, let’s replace the markup returned by the component with the following:

```javascript
//backendforfrontend.client/src/App.jsx

//...existing code...

return (
    <div>
        <h1 id="tableLabel">Weather forecast</h1>
        <p>This component demonstrates fetching data from the server.</p>

        <hr/>

        {isAuthenticated ? (
          <div>
            <div>
              <Profile />
            </div>

            <hr />

            <h2>Weather data</h2>
            <div>
            {contents}
            </div>

            <hr />

            <LogoutButton />
          </div>
        ) : (
          <div className="action-card">
            <p className="action-text">Get started by signing in to your account</p>
            <LoginButton />
          </div>
        )}
    </div>
);

//...existing code...
```

The new markup conditionally renders based on `isAuthenticated`. When not authenticated the app shows only the login button. When authenticated it shows the user's profile, the weather table, and the logout button. 

## Run the Web Application

We are ready to run the application\!

If you are using Visual Studio, you can click the *Run* button to have all the three projects running at once.

If you are using the .NET CLI, start the projects in a terminal window in this order:

```shell
cd WeatherForecastAPI
dotnet run
cd ../BackendForFrontend.Server
dotnet run
```

The last command also auto-starts the Vite dev server via `SpaProxy`.

## About the Login Flow

Here is what happens when the user authenticates with the application we have built:

* The user clicks the *Log In* button and this calls the `/bff/login` endpoint.  
* The Auth0 authentication middleware redirects the user to the Auth0's authorization server, which displays the [Universal Login page](https://auth0.com/docs/universal-login).  
* The user can log in with their username and password, social provider, or any other identity provider.  
* Once the user has logged in, Auth0 calls back to the `/callback` endpoint in your BFF application and passes along an authorization code.  
* The Auth0 authentication middleware exchanges the authorization code for the user's ID and access token. It extracts the user information from the claims in the ID token, returns a successful authentication response, and sets a cookie that indicates that the user is authenticated.  
* The React application uses the authentication context to issue an API call to the `/bff/getUser` endpoint. This endpoint returns the user authentication state and user claims. It also calls the `/bff/weatherforecast` endpoint, which gets the response of the remote API via the BFF.  
* The React application renders the UI components using the authenticated user's identity.

## Conclusion

The BFF pattern is an ideal solution for authentication if you can afford a dedicated backend. It will help you avoid headaches when dealing with access tokens and how to keep them safe on your client-side application. The backend will do all the heavy lifting, so that you can focus only on UI/UX concerns in the frontend.

For production, consider adding [token refresh handling](https://auth0.com/blog/use-refresh-tokens-in-aspnet-core-apps/), [CSRF protection](https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery?view=aspnetcore-10.0#afwma), and [rate limiting](https://learn.microsoft.com/en-us/aspnet/core/performance/rate-limit) to your BFF endpoints.

You can download from [this GitHub repository](https://github.com/auth0-blog/bff-auth0-dotnet) the full source code of the sample project built in this article.