Local APIs

A local API can be any invocable functionality that is located inside the BFF host, for example as an MVC controller or just a simple endpoint.

These endpoints need to be secured to make sure that only the frontend can call them. We recommend doing this using two layers of protection:

SameSite cookies

SameSite cookies are a built-in feature of modern browsers to make sure that a cookie only gets sent from a page that originates from the same site where the cookie was originally issued on.

This is a good first layer of defense, but makes the assumption that you can trust all sub-domains of your top-level site, for example *.mycompany.com.

Anti-forgery header

In addition to the cookie protection, we recommend requiring an additional custom header, for example:

GET /endpoint

x-csrf: 1

The fact that the header value is static is really not important. Its presence in combination with the cookie requirement will trigger CORS preflight request for cross-origin calls. This effectively sandboxes the caller to the same origin as the backend which is a very strong security guarantee.

In addition, API endpoints also need some special treatment in situations where the session has expired, or authorization fails. In these cases you want to avoid trigger an authentication redirect to the upstream IdP, but instead return Ajax-friendly status codes


Duende.BFF can automate above pre/post-processing of API endpoints. For that you need to add the BFF middleware to the pipeline:

public void Configure(IApplicationBuilder app)
    // rest omitted




    app.UseEndpoints(endpoints => { ... }

The BFF middleware must be placed before the authorization middleware.

In addition, the endpoints that need the processing must be decorated, e.g.:

app.UseEndpoints(endpoints =>
    // MVC controllers
        .RequireAuthorization()    // no anonymous access
        .AsBffApiEndpoint();       // BFF pre/post processing

    // simple endpoint
    endpoints.MapPost("/foo", context => { ... })

Or if using MVC, then an attribute applied directly to the controller or action:

public class MyApiController : ControllerBase
{ ... }

You can disable the anti-forgery protection requirement by setting the requireAntiForgeryCheck parameter to false on the endpoint or attribute. This is not recommended.