Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 0 additions & 100 deletions astro/src/content/docs/bff/architecture/index.md

This file was deleted.

193 changes: 193 additions & 0 deletions astro/src/content/docs/bff/architecture/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: "Architecture"
description: Overview of BFF host architecture, including authentication, session management, and integration with ASP.NET Core components
date: 2020-09-10T08:22:12+02:00
sidebar:
order: 1
label: "Overview"
redirect_from:
- /bff/v2/architecture/
- /bff/v3/architecture/
- /identityserver/v5/bff/architecture/
- /identityserver/v6/bff/architecture/
- /identityserver/v7/bff/architecture/
---

import { CardGrid, LinkCard } from "@astrojs/starlight/components";

A BFF host is an ASP.NET Core application that acts as a security proxy between the browser and your backend APIs. Understanding the key architectural decisions up front will save you significant rework later.

:::tip[New to BFF?]
If you haven't yet decided whether to use BFF, start with the [overview](/bff/) which covers the threat model and the BFF-vs-token-in-browser comparison.
:::

## How the BFF Fits Into Your System

The following diagram shows how the BFF protects browser-based applications:

```mermaid
flowchart TD
subgraph Browser
SPA["Browser-Based Application"]
CookieJar["🍪 Cookie Jar"]
end

subgraph BFF["BFF Host"]
AuthEndpoints["Authentication<br/>Endpoints"]
SessionMgmt["Session<br/>Management"]
CookieAuth["Cookie Authorization"]
CSRF["CSRF Protection"]
Proxy["Proxy to<br/>External APIs"]
LocalAPIs["Local APIs"]
SessionStore[("Server-Side<br/>Session Storage")]
end

IdP["Identity Provider"]
ExternalAPIs["External APIs"]

SPA -->|"login / logout"| AuthEndpoints
AuthEndpoints -->|"Set-Cookie"| CookieJar
CookieJar -->|"Auth cookie"| CookieAuth
AuthEndpoints <-->|"redirect"| IdP
AuthEndpoints --> SessionMgmt
SessionMgmt --> SessionStore
CookieAuth --> CSRF
CSRF --> Proxy
CSRF --> LocalAPIs
Proxy -->|"Bearer token"| ExternalAPIs
SessionMgmt -->|"Acquire tokens"| IdP
ExternalAPIs -->|"Validate tokens"| IdP
```

The BFF sits between the browser and everything else. The browser only ever holds a **session cookie** — it never sees tokens. The BFF exchanges that cookie for bearer tokens when forwarding requests to downstream APIs.

## Architectural Decisions

### Decision 1: Where Does Your UI Live?

The simplest setup hosts both the UI assets and the BFF from the **same origin**. This makes cookies same-site, eliminates CORS, and avoids [third-party cookie blocking](/bff/architecture/third-party-cookies.md).

You can also run the frontend on a **separate origin** (e.g. a Vite dev server, a CDN) and point it at the BFF via CORS. This is more complex but enables independent deployment.

<LinkCard
href="/bff/architecture/ui-hosting/"
title="UI Hosting"
description="Full comparison of same-origin vs. separate-origin hosting"
/>

### Decision 2: Cookie-Only vs. Server-Side Sessions

By default, the BFF stores the entire session in the cookie. This is simple and stateless but has limits: cookie size, no server-side revocation.

With **server-side sessions**, the cookie holds only a session ID. The server stores the session state (typically in a database or distributed cache). This enables:
- Forced logout across all sessions
- Back-channel logout from the identity provider
- Querying active sessions

<LinkCard
href="/bff/fundamentals/session/server-side-sessions/"
title="Server-Side Sessions"
description="Configure server-side session storage for scalability and revocation"
/>

### Decision 3: How Do You Expose APIs?

| API Pattern | When to Use |
|---|---|
| **Local API** | Business logic hosted inside the BFF process itself. Lowest latency, no token forwarding needed. |
| **Remote API (direct)** | External microservice. BFF forwards the request with a bearer token attached. |
| **Remote API (YARP)** | External microservice with complex routing rules. BFF uses YARP as the reverse proxy. |

<LinkCard
href="/bff/fundamentals/apis/"
title="API Types"
description="Decision flowchart for choosing the right API pattern"
/>

### Decision 4: Single Frontend vs. Multi-Frontend

Each BFF instance is tied to **one** browser-based application and **one** OIDC client registration. If you have multiple frontends (e.g. a customer portal and an admin app), run separate BFF instances with separate client IDs. They can share infrastructure (same process, different routes) but should not share session state or token storage.

<LinkCard
href="/bff/fundamentals/options/#common-configurations"
title="Common Configurations"
description="Multi-frontend configuration example"
/>

### Decision 5: Blazor or JavaScript?

Both are supported, but have different integration patterns:

- **JavaScript SPAs** interact with the BFF via `/bff/user`, `/bff/login`, `/bff/logout`, and API endpoints
- **Blazor** uses built-in `AuthenticationStateProvider` integration and can call APIs server-side (no token forwarding from browser)

<LinkCard
href="/bff/fundamentals/blazor/"
title="Blazor Fundamentals"
description="Blazor-specific guidance for rendering modes, data access, and auth state"
/>

## Trust Boundaries

```mermaid
flowchart TD
subgraph Browser["Browser (untrusted)"]
B1["Holds session cookie only<br/>(HttpOnly, Secure, SameSite)"]
B2["Never sees access or refresh tokens"]
end

subgraph BFF["BFF Host (trusted server)"]
BFF1["Validates session cookie on every request"]
BFF2["Manages access/refresh tokens in server memory or DB"]
BFF3["Enforces anti-forgery (X-CSRF header) on API routes"]
end

subgraph IdP["Identity Provider<br/>(e.g. IdentityServer)"]
end

subgraph APIs["Downstream APIs<br/>(microservices, external)"]
end

Browser -->|"HTTPS + Cookie"| BFF
BFF -->|"OIDC/OAuth (HTTPS)"| IdP
BFF -->|"Bearer token (HTTPS)"| APIs
```

The critical security property: **tokens never cross the trust boundary into the browser**. All token operations happen server-to-server.

## Internals

Duende.BFF is built on top of:

| Component | Role | Details |
|---|---|---|
| ASP.NET OIDC handler | Protocol processing (auth code + PKCE, token exchange) | Standard ASP.NET middleware |
| ASP.NET Cookie handler | Session management and cookie issuance | Extended by BFF for server-side sessions |
| Duende.AccessTokenManagement | Token storage, refresh, revocation | [Docs](/accesstokenmanagement/index.mdx) |
| YARP | Reverse proxy for remote APIs | [BFF YARP integration](/bff/fundamentals/apis/yarp.md) |

## See Also

<CardGrid>
<LinkCard
href="/identityserver/fundamentals/clients/"
title="IdentityServer Client Configuration"
description="Register your BFF as a confidential OIDC client"
/>
<LinkCard
href="/bff/architecture/third-party-cookies/"
title="Third-Party Cookies"
description="How browser cookie restrictions affect BFF architecture"
/>
<LinkCard
href="/bff/architecture/ui-hosting/"
title="UI Hosting"
description="Options for hosting the frontend alongside the BFF"
/>
<LinkCard
href="/bff/fundamentals/middleware-pipeline/"
title="Middleware Pipeline"
description="Canonical middleware order reference"
/>
</CardGrid>

12 changes: 11 additions & 1 deletion astro/src/content/docs/bff/architecture/multi-frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,17 @@ BFF V4 still allows you to manually configure the ASP.NET Core authentication op

To achieve this, the BFF automatically configures the ASP.NET Core pipeline:

![BFF Multi-Frontend Pipeline](../images/bff_multi_frontend_pipeline.svg)
```mermaid
---
title: BFF Middleware Pipeline
---
flowchart TD
A["FrontendSelectionMiddleware"] --> B["PathMappingMiddleware"]
B --> C["OpenIdCallbackMiddleware"]
C --> D["Your ASP.NET Core Pipeline"]:::app
D --> E["MapRemoteRoutesMiddleware"]
E --> F["ProxyIndexMiddleware"]
```

1. `FrontendSelectionMiddleware` - This middleware performs the frontend selection by seeing which frontend's selection criteria best matches the incoming request route. It's possible to mix both path based routing and host based routing, so the most specific will be selected.
2. `PathMappingMiddleware` - If you use path mapping, in the selected frontend, then it will automatically map the frontend's path so none of the subsequent middlewares know (or need to care) about this fact.
Expand Down
65 changes: 62 additions & 3 deletions astro/src/content/docs/bff/architecture/ui-hosting.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,27 @@ automatically include the authentication cookie and not require CORS
headers. This makes the BFF and the front-end application a single deployable unit. Below shows a graphical overview of
what that would look like:

![Hosting BFF UI from the UI](../images/bff_ui_hosting_loc.svg)
```mermaid
flowchart LR
subgraph Browser["Browser: https://application.url"]
app["app"]
end

subgraph BFF["BFF Application"]
endpoints["BFF endpoints<br/>local / remote API endpoints"]
static["Static files middleware"]
end

subgraph FS["Local Filesystem"]
index["index.html"]
scripts["script_assets.js"]
images["images"]
end

app -->|"cookie"| endpoints
app --> static
static --> FS
```

If you create a BFF host using our templates, the UI will be hosted in this way:

Expand Down Expand Up @@ -58,7 +78,25 @@ outside of Visual Studio (e.g., using the node cli). You might also want to have
and the BFF, and you might want your static UI assets hosted on a CDN. Below is a schematic overview of what that would
look like:

![Hosting BFF UI on CDN](../images/bff_ui_hosting_cdn.svg)
```mermaid
flowchart LR
subgraph Browser["Browser: https://application.url"]
app["app"]
end

subgraph BFF["BFF Application (https://bff.url)"]
endpoints["BFF endpoints<br/>local / remote API endpoints"]
end

subgraph CDN["CDN"]
index["index.html"]
scripts["script_assets.js"]
images["images"]
end

app -->|"cookie + CORS"| endpoints
app -->|"load assets"| CDN
```

The browser accesses the application via the BFF. The BFF proxies the calls to index.html to the CDN. The browser can
then download all static assets from the CDN, but then use the BFF (and it’s API’s and user management API’s) secured by
Expand Down Expand Up @@ -90,7 +128,28 @@ another host (presumably a CDN). This technique makes the UI and BFF have exactl
cookie will be sent from the frontend to the BFF automatically, and third party cookie blocking and the SameSite cookie
attribute won't present any problems. The following diagram shows how that would work:

![BFF Proxies the Index html from CDN](../images/bff_ui_hosting_proxy_index.svg)
```mermaid
flowchart LR
subgraph Browser["Browser: https://application.url"]
app["app"]
end

subgraph BFF["BFF Application"]
endpoints["BFF endpoints<br/>local / remote API endpoints"]
proxy["proxy"]
end

subgraph CDN["CDN (https://the.cdn)"]
index["index.html"]
scripts["script_assets.js"]
images["images"]
end

app -->|"cookie"| endpoints
app -->|"initial request"| proxy
proxy -->|"proxy index.html"| CDN
app -->|"load assets"| CDN
```

Setting this up for local development takes a bit of effort, however. As you make changes to the frontend, the UI's build
process might generate a change to the index page. If it does, you'll need to arrange for the index page being served by
Expand Down
Loading
Loading