duende-bff
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.
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 disablRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.