# YARP extensions

> Integration of Duende.BFF with Microsoft's YARP reverse proxy, including token management and anti-forgery protection features.

Duende.BFF integrates with Microsoftâs full-featured reverse proxy [YARP](https://microsoft.github.io/reverse-proxy/).

YARP includes many advanced features such as load balancing, service discovery, and session affinity. It also has its own extensibility mechanism. Duende.BFF includes YARP extensions for token management and anti-forgery protection so that you can combine the security and identity features of `Duende.BFF` with the flexible reverse proxy features of YARP.

## Adding YARP

[Section titled âAdding YARPâ](#adding-yarp)

To enable Duende.BFFâs YARP integration, add a reference to the *Duende.BFF.Yarp* NuGet package to your project and add YARP and the BFFâs YARP extensions to DI:

```csharp
builder.Services.AddBff();


// adds YARP with BFF extensions
var yarpBuilder = services.AddReverseProxy()
    .AddBffExtensions();
```

## Configuring YARP

[Section titled âConfiguring YARPâ](#configuring-yarp)

YARP is most commonly configured by a config file. The following simple example forwards a local URL to a remote API:

```json
{
  "ReverseProxy": {
    "Routes": {
      "todos": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/todos/{**catch-all}"
        }
      }
    },
    "Clusters": {
      "cluster1": {
        "Destinations": {
          "destination1": {
            "Address": "https://API.mycompany.com/todos"
          }
        }
      }
    }
  }
}
```

See the Microsoft [documentation](https://microsoft.github.io/reverse-proxy/articles/config-files.html) for the complete configuration schema.

Another option is to configure YARP in code using the in-memory config provider included in the BFF extensions for YARP. The above configuration as code would look like this:

```csharp
yarpBuilder.LoadFromMemory(
    new[]
    {
        new RouteConfig()
        {
            RouteId = "todos",
            ClusterId = "cluster1",


            Match = new()
            {
                Path = "/todos/{**catch-all}"
            }
        }
    },
    new[]
    {
        new ClusterConfig
        {
            ClusterId = "cluster1",


            Destinations = new Dictionary<string, DestinationConfig>(StringComparer.OrdinalIgnoreCase)
            {
                {
                    "destination1", new()
                    {
                        Address = "https://API.mycompany.com/todos"
                    }
                },
            }
        }
    });
```

## Token Management

[Section titled âToken Managementâ](#token-management)

Duende.BFFâs YARP extensions provide access token management and attach user or client access tokens automatically to proxied API calls. To enable this, add metadata with the name *Duende.Bff.Yarp.TokenType* to the route or cluster configuration:

```json
{
  "ReverseProxy": {
    "Routes": {
      "todos": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/todos/{**catch-all}"
        },
        "Metadata": {
          "Duende.Bff.Yarp.TokenType": "User"
        }
      }
    }
  }
}
```

Similarly to the [simple HTTP forwarder](/bff/fundamentals/apis/remote/#access-token-requirements), the allowed values for the token type are *User*, *Client*, *UserOrClient*.

Routes that set the *Duende.Bff.Yarp.TokenType* metadata **require** the given type of access token. If it is unavailable (for example, if the *User* token type is specified but the request to the BFF is anonymous), then the proxied request will not be sent, and the BFF will return an HTTP 401: Unauthorized response.

If you are using the code config method, call the *WithAccessToken* extension method to achieve the same thing:

```csharp
yarpBuilder.LoadFromMemory(
    new[]
    {
        new RouteConfig()
        {
            RouteId = "todos",
            ClusterId = "cluster1",


            Match = new RouteMatch
            {
                Path = "/todos/{**catch-all}"
            }
        }.WithAccessToken(TokenType.User)
    },
    // rest omitted
);
```

Again, the *WithAccessToken* method causes the route to require the given type of access token. If it is unavailable, the proxied request will not be made and the BFF will return an HTTP 401: Unauthorized response.

## Optional User Access Tokens

[Section titled âOptional User Access Tokensâ](#optional-user-access-tokens)

You can also attach user access tokens optionally by adding metadata named âDuende.Bff.Yarp.OptionalUserTokenâ to a YARP route.

```json
{
  "ReverseProxy": {
    "Routes": {
      "todos": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/todos/{**catch-all}"
        },
        "Metadata": {
          "Duende.Bff.Yarp.OptionalUserToken": "true"
        }
      }
    }
  }
}
```

This metadata causes the userâs access token to be sent with the proxied request when the user is logged in, but makes the request anonymously when the user is not logged in. It is an error to set both *Duende.Bff.Yarp.TokenType* and *Duende.Bff.Yarp.OptionalUserToken*, since they have conflicting semantics (*TokenType* requires the token, *OptionalUserToken* makes it optional).

If you are using the code config method, call the *WithOptionalUserAccessToken* extension method to achieve the same thing:

```csharp
yarpBuilder.LoadFromMemory(
    new[]
    {
        new RouteConfig()
        {
            RouteId = "todos",
            ClusterId = "cluster1",


            Match = new RouteMatch
            {
                Path = "/todos/{**catch-all}"
            }
        }.WithOptionalUserAccessToken()
    },
    // rest omitted
);
```

### Anti-forgery Protection

[Section titled âAnti-forgery Protectionâ](#anti-forgery-protection)

Duende.BFFâs YARP extensions can also add anti-forgery protection to proxied API calls. Anti-forgery protection defends against CSRF attacks by requiring a custom header on API endpoints, for example:

```plaintext
GET /endpoint


x-csrf: 1
```

The value of the header is not important, but its presence, combined with the cookie requirement, triggers a CORS preflight request for cross-origin calls. This effectively isolates the caller to the same origin as the backend, providing a robust security guarantee.

You can add the anti-forgery protection to all YARP routes by calling the *AsBffApiEndpoint* extension method:

```cs
app.MapReverseProxy()
    .AsBffApiEndpoint();


// or shorter
app.MapBffReverseProxy();
```

If you need more fine-grained control over which routes should enforce the anti-forgery header, you can also annotate the route configuration by adding the *Duende.Bff.Yarp.AntiforgeryCheck* metadata to the route config:

```json
{
  "ReverseProxy": {
    "Routes": {
      "todos": {
        "ClusterId": "cluster1",
        "Match": {
          "Path": "/todos/{**catch-all}"
        },
        "Metadata": {
          "Duende.Bff.Yarp.AntiforgeryCheck": "true"
        }
      }
    }
  }
}
```

This is also possible in code:

```cs
yarpBuilder.LoadFromMemory(
    new[]
    {
        new RouteConfig()
        {
            RouteId = "todos",
            ClusterId = "cluster1",


            Match = new RouteMatch
            {
                Path = "/todos/{**catch-all}"
            }
        }.WithAntiforgeryCheck()
    },
    // rest omitted
);
```

Note

You can combine the token management feature with the anti-forgery check.

To enforce the presence of the anti-forgery headers, you need to add a middleware to the YARP pipeline:

Program.cs

```cs
app.MapReverseProxy(proxyApp =>
{
    proxyApp.UseAntiforgeryCheck();
});
```
-----