Claude
Skills
Sign in
Back

duende-bff

Included with Lifetime
$97 forever

Duende BFF (Backend for Frontend) security framework for securing SPAs. Covers session management, API endpoint proxying, token management, anti-forgery protection, and integration with React/Angular/Blazor frontends.

Web Dev

What this skill does


# Duende BFF (Backend for Frontend)

## When to Use This Skill

- Building or securing a SPA (React, Angular, Vue, Blazor WASM) that calls APIs requiring authentication
- Implementing the Backend-for-Frontend security pattern to keep access tokens out of the browser
- Configuring BFF session management, login/logout endpoints, and server-side sessions
- Proxying requests from a frontend to remote APIs while automatically attaching access tokens
- Adding CSRF/anti-forgery protection to APIs consumed by browser-based applications
- Integrating `Duende.BFF` with `Duende.AccessTokenManagement` for automatic token refresh
- Deploying a BFF behind a reverse proxy or configuring same-site cookie behavior

## Core Principles

1. **Tokens Never Touch the Browser** — The BFF holds all OAuth tokens server-side; the browser only ever sees an HTTP-only, Secure, SameSite cookie
2. **CSRF Protection Is Mandatory** — Every BFF API endpoint must require the `X-CSRF: 1` header; use `.AsBffApiEndpoint()` or `MapRemoteBffApiEndpoint` — never skip it without an explicit alternative
3. **Cookie Configuration Determines Security Posture** — `SameSite=Strict` is preferred when the IDP is on the same site; `Lax` is acceptable when cross-site redirects are required after login
4. **Server-Side Sessions for Production** — The default in-memory cookie session is unsuitable for production; persist sessions with `Duende.BFF.EntityFramework`
5. **Token Management Is Automatic** — BFF integrates with `Duende.AccessTokenManagement`; never manually refresh tokens or pass raw access tokens to the frontend

Docs: https://docs.duendesoftware.com/identityserver/bff

---

## Pattern 1: Setup and Registration (BFF v4)

BFF v4 uses a streamlined registration API that auto-configures OpenID Connect and cookie authentication with recommended defaults.

```csharp
// ✅ v4: AddBff() with fluent OIDC and cookie configuration
builder.Services.AddBff()
    .ConfigureOpenIdConnect(options =>
    {
        options.Authority = "https://your-idp.example.com";
        options.ClientId = "my-bff-client";
        options.ClientSecret = "secret";
        options.ResponseType = "code";
        options.ResponseMode = "query";

        options.GetClaimsFromUserInfoEndpoint = true;
        options.SaveTokens = true;
        options.MapInboundClaims = false;

        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");
        options.Scope.Add("offline_access"); // Required for refresh tokens
    })
    .ConfigureCookies(options =>
    {
        // Use Strict when your IDP is on the same site as the BFF.
        // Use Lax when a cross-site redirect is required (e.g., IDP on a different domain).
        options.Cookie.SameSite = SameSiteMode.Lax;
    });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseRouting();
app.UseAuthentication();
app.UseBff();          // Adds CSRF anti-forgery enforcement middleware
app.UseAuthorization();

app.Run();
```

```csharp
// ❌ v4: Do NOT manually wire AddCookie + AddOpenIdConnect when using AddBff()
// ConfigureOpenIdConnect and ConfigureCookies handle this correctly
builder.Services.AddAuthentication()
    .AddCookie("cookie")
    .AddOpenIdConnect("oidc", ...); // Bypasses BFF's recommended defaults
```

### BFF v3 Registration

For projects still on v3, explicit scheme setup is required and `MapBffManagementEndpoints()` must be called manually:

```csharp
// ✅ v3: explicit authentication scheme wiring
builder.Services.AddBff();

builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = "cookie";
        options.DefaultChallengeScheme = "oidc";
        options.DefaultSignOutScheme = "oidc";
    })
    .AddCookie("cookie", options =>
    {
        options.Cookie.Name = "__Host-bff";
        options.Cookie.SameSite = SameSiteMode.Strict;
    })
    .AddOpenIdConnect("oidc", options =>
    {
        options.Authority = "https://your-idp.example.com";
        options.ClientId = "my-bff-client";
        options.ClientSecret = "secret";
        options.ResponseType = "code";
        options.SaveTokens = true;
        options.Scope.Add("offline_access");
    });

// ...

app.MapBffManagementEndpoints(); // ✅ Required in v3
```

### Key Differences: V4 vs V3

| Feature               | V4                                                | V3                                          |
| --------------------- | ------------------------------------------------- | ------------------------------------------- |
| Auth handler setup    | `ConfigureOpenIdConnect()` / `ConfigureCookies()` | Manual `AddCookie()` / `AddOpenIdConnect()` |
| Management endpoints  | Auto-registered                                   | `MapBffManagementEndpoints()` required      |
| Remote API token type | `.WithAccessToken(RequiredTokenType.User)`        | `.RequireAccessToken(TokenType.User)`       |
| Session cleanup       | `.AddSessionCleanupBackgroundProcess()`           | `EnableSessionCleanup` option               |
| Token retriever       | `IAccessTokenRetriever` (implement directly)      | `DefaultAccessTokenRetriever` (inheritable) |
| Multi-frontend        | Built-in `AddFrontend()` API                      | Not supported                               |
| Middleware control     | `AutomaticallyRegisterBffMiddleware` option       | Always automatic                            |

---

## Pattern 2: Login and Logout Endpoints

In BFF v4, management endpoints (`/bff/login`, `/bff/logout`, `/bff/user`, `/bff/backchannel-logout`) are registered automatically by `AddBff()` with the implicit default frontend. In v3, they require an explicit call to `MapBffManagementEndpoints()`.

**Login** — A browser navigation to `/bff/login` initiates an OIDC Authorization Code flow. After the IDP redirects back, the BFF sets an HTTP-only authentication cookie.

```csharp
// ✅ Trigger login from the SPA (browser navigation, not fetch)
// React example:
// window.location.href = '/bff/login?returnUrl=/dashboard';

// ✅ Optional: supply a returnUrl to redirect after login
// GET /bff/login?returnUrl=/dashboard
// The returnUrl must be a local path; absolute URLs are rejected.
```

**Logout** — A browser navigation to `/bff/logout` signs the user out locally and initiates an OIDC end_session flow. It also revokes the refresh token automatically.

```csharp
// ✅ The sid claim from /bff/user must be passed as a query parameter
// GET /bff/logout?sid=<session-id>
// This is required to prevent CSRF attacks on the logout endpoint.
```

```csharp
// ❌ Do NOT call /bff/logout via fetch() without the sid parameter.
// The logout endpoint validates the sid to prevent cross-site logout attacks.
```

---

## Pattern 3: CSRF / Anti-Forgery Protection

The BFF enforces a custom `X-CSRF` header on every protected endpoint. This triggers a CORS preflight for cross-origin requests, effectively preventing CSRF attacks. The header value is irrelevant — its presence is sufficient.

### Local (Embedded) API Endpoints

```csharp
// ✅ Minimal API: decorate with AsBffApiEndpoint()
app.MapGet("/api/data", (HttpContext ctx) => Results.Ok("data"))
    .RequireAuthorization()
    .AsBffApiEndpoint();

// ✅ MVC Controllers: apply to the entire controller via attribute
[Route("api/data")]
[BffApi]
public class DataController : ControllerBase
{
    [HttpGet]
    public IActionResult Get() => Ok("data");
}

// ✅ MVC Controllers: apply at mapping time
app.MapControllers()
    .RequireAuthorization()
    .AsBffApiEndpoint();
```

```csharp
// ❌ Do NOT expose BFF API endpoints without AsBffApiEndpoint() or BffApi attribute.
// Without it, the x-csrf header is not enforced and the endpoint is CSRF-vulnerable.
app.MapGet("/api/data", () => Results.Ok("data"))
    .RequireAuthorization(); // Missing .AsBffApiEndpoint()
```

### Middleware Order

`UseBff()` must appear **after** `UseRouting()` but **before** `UseAuthorization()`. Incorrect order silently disabl
Files: 1
Size: 36.5 KB
Complexity: 39/100
Category: Web Dev

Related in Web Dev