Skip to content

Commit d881cb1

Browse files
authored
Merge pull request #820 from DuendeSoftware/ev/bff/v4
BFF V4 docs
2 parents 97aa8c8 + 54594d1 commit d881cb1

37 files changed

+1295
-372
lines changed

src/content/docs/accesstokenmanagement/advanced/client-credentials.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ You can add token client definitions to your host while configuring the DotNet s
5555
client.Resource = "urn:invoices";
5656
});`}/>
5757
</TabItem>
58-
</Tabs>
58+
</Tabs>
59+
5960
You can set the following options:
6061

6162
* `TokenEndpoint` - URL of the OAuth token endpoint where this token client requests tokens from

src/content/docs/bff/architecture/index.md

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,51 +22,63 @@ functions:
2222
* Server-side Token Management
2323
* Blazor support with unified authentication state management across rendering modes.
2424

25+
## Authentication Flow
26+
27+
The following diagram shows how the BFF protects browser-based applications:
28+
2529
![BFF Security Framework Architecture Overview](../images/bff_application_architecture.svg)
2630

31+
32+
* **Authentication flows**: The server handles the authentication flows. There are specific endpoints for login / logout. While the browser is involved with these authentication flows, because the user is redirected to and from the identity provider, the browser-based application will never see the authentication tokens. These are exchanged for a code on the server only.
33+
* **Cookies**: After successful authentication, a cookie is added. This cookie protects all subsequent calls to the APIs. When using this type of authentication, **CSRF protection** is very important.
34+
* **Access to APIs**: The BFF can expose embedded APIs (which are hosted by the BFF itself) or proxy calls to remote APIs (which is more common in a microservice environment). While proxying, it will exchange the authentication cookie for an access token.
35+
* **Session Management**: The BFF can manage the users session. This can either be cookie-based session management or storage-based session management.
36+
37+
38+
## Internals
2739
Duende.BFF builds on widely used tools and frameworks, including ASP.NET Core's OpenID Connect and cookie authentication
28-
handlers, YARP, and Duende.AccessTokenManagement. Duende.BFF combines these tools and adds additional security and
40+
handlers, YARP, and [Duende.AccessTokenManagement](/accesstokenmanagement/index.mdx). Duende.BFF combines these tools and adds additional security and
2941
application features that are useful with a BFF architecture so that you can focus on providing application logic
3042
instead of security logic:
3143

3244
![Duende BFF Security Framework - components](../images/bff_blocs.svg)
3345

34-
## ASP.NET OpenID Connect Handler
46+
### ASP.NET OpenID Connect Handler
3547

3648
Duende.BFF uses ASP.NET's OpenID Connect handler for OIDC and OAuth protocol processing. As long-term users of and
3749
contributors to this library, we think it is a well implemented and flexible implementation of the protocols.
3850

39-
## ASP.NET Cookie Handler
51+
### ASP.NET Cookie Handler
4052

41-
Duende.BFF uses ASP.NET's Cookie handler for session management. The Cookie handler provides a claims-based identity to
53+
Duende.BFF uses ASP.NET's Cookie handler for session management. The cookie handler provides a claims-based identity to
4254
the application persisted in a digitally signed and encrypted cookie that is protected with modern cookie security
4355
features, including the Secure, HttpOnly and SameSite attributes. The handler also provides absolute and sliding session
4456
support, and has a flexible extensibility model, which Duende.BFF uses to
4557
implement [server-side session management](/bff/fundamentals/session/server-side-sessions/)
4658
and [back-channel logout support](/bff/fundamentals/session/management/back-channel-logout/).
4759

48-
## Duende.AccessTokenManagement
60+
### Duende.AccessTokenManagement
4961

5062
Duende.BFF uses the Duende.AccessTokenManagement library for access token management and storage. This includes storage
5163
and retrieval of tokens, refreshing tokens as needed, and revoking tokens on logout. The library provides integration
5264
with the ASP.NET HTTP client to automatically attach tokens to outgoing HTTP requests, and its underlying management
5365
actions can also be programmatically invoked through an imperative API.
5466

55-
## API Endpoints
67+
### API Endpoints
5668

5769
In the BFF architecture, the frontend makes API calls to backend services via the BFF host exclusively. Typically, the
5870
BFF acts as a reverse proxy to [remote APIs](/bff/fundamentals/apis/remote), providing session and token management.
5971
Implementing local APIs within the BFF host is also [possible](/bff/fundamentals/apis/local). Regardless, requests to
6072
APIs are authenticated with the session cookie and need to be secured with an anti-forgery protection header.
6173

62-
## YARP
74+
### YARP
6375

6476
Duende.BFF proxies requests to remote APIs using Microsoft's YARP (Yet Another Reverse Proxy). You can set up YARP using
6577
a simplified developer-centric configuration API provided by Duende.BFF, or if you have more complex requirements, you
6678
can use the full YARP configuration system directly. If you are using YARP directly, Duende.BFF
6779
provides [YARP integration](/bff/fundamentals/apis/yarp) to add BFF security and identity features.
6880

69-
## UI Assets
81+
### UI Assets
7082

7183
The BFF host typically serves at least some of the UI assets of the frontend, which can be HTML/JS/CSS, WASM, and/or
7284
server-rendered content. Serving the UI assets, or at least the index page of the UI from the same origin as the backend
@@ -78,7 +90,7 @@ SameSite cookie attribute prevents the frontend from sending the authentication
7890
It is also possible to separate the BFF and UI and host them separately. See [here](/bff/architecture/ui-hosting) for
7991
more discussion of UI hosting architecture.
8092

81-
## Blazor Support
93+
### Blazor Support
8294

8395
Blazor based applications have unique challenges when it comes to authentication state. It's possible to mix various
8496
rendering models in a single application. Auto mode even starts off server rendered, then transitions to WASM when the

src/content/docs/bff/architecture/multi-frontend.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ The Duende BFF V4 library doesn't ship with an abstraction to store or read fron
2929

3030
## A Typical Example
3131

32-
Consider an enterprise that hosts multiple browser based applications. Each of these applications is developed by a separate team and as such, has it's own deployment schedule.
32+
Consider an enterprise that hosts multiple browser based applications. Each of these applications is developed by a separate team and as such, has its own deployment schedule.
3333

3434
There are some internal-facing applications that are exclusively used by internal employees. These internal employees are all present in Microsoft Entra ID, so these internal-facing applications should directly authenticate against Microsoft Entra ID. These applications also use several internal APIs, that due to the sensitivity, should not be accessible by external users. However, they also use some of the more common APIs. These apps are only accessible via an internal DNS name, such as `https://app1.internal.example.com`.
3535

@@ -60,6 +60,41 @@ After your application's logic is executed, there are two middlewares registered
6060

6161
5. `MapRemoteRoutesMiddleware` - This will handle any configured remote routes. Note, it will not handle plain YARP calls, only routes that are specifically added to a frontend.
6262

63-
6. `ProxyIndexMiddleware` - If configured, this proxy the `index.html` to start the browser based app.
63+
6. `ProxyIndexMiddleware` - If configured, this proxies the `index.html` to start the browser based app.
6464

65+
If you don't want this automatic mapping of BFF middleware, you can turn it off using `BffOptions.AutomaticallyRegisterBffMiddleware`. When doing so, you'll need to manually register and add the middlewares:
66+
67+
```csharp
68+
var app = builder.Build();
69+
70+
app.UseBffFrontendSelection();
71+
app.UseBffPathMapping();
72+
app.UseBffOpenIdCallbacks();
73+
74+
// TODO: your custom middleware goes here
75+
app.UseRouting();
76+
app.UseBff();
77+
78+
// NOTE: Only add this if you want to proxy remote APIs.
79+
app.UseBffRemoteRoutes();
80+
81+
app.MapBffManagementEndpoints();
82+
app.UseBffIndexPages();
83+
84+
app.Run();
85+
```
86+
87+
## Authentication Architecture
88+
89+
When you use multiple frontends, you can't rely on [manual authentication configuration](../fundamentals/session/handlers.mdx#manually-configuring-authentication). This is because each frontend requires its own scheme, and potentially its own OpenID Connect and Cookie configuration.
90+
91+
The BFF registers a dynamic authentication scheme, which automatically configures the OpenID Connect and Cookie Scheme's on behalf of the frontends. It does this using a custom `AuthenticationSchemeProvider` called `BffAuthenticationSchemeProvider` to return appropriate authentication schemes for each frontend.
92+
93+
The BFF will register two schemes:
94+
* `duende-bff-oidc`
95+
* `duende-bff-cookie`
96+
97+
Then, if there are no default authentication schemes registered, it will register 'duende_bff_cookie' schemes as the `AuthenticationOptions.DefaultScheme`, and 'duende_bff_oidc' as the `AuthenticationOptions.DefaultAuthenticateScheme` and `AuthenticationOptions.DefaultSignOutScheme`. This will ensure that calls to `Authenticate()` or `Signout()` will use the appropriate schemes.
98+
99+
If you're using multiple frontends, then the BFF will create dynamic schemes with the following signature: `duende_bff_oidc_[frontendname]` and `duende_bff_cookie_[frontendname]`. This ensures that every frontend can use its own OpenID Connect and Cookie settings.
65100

src/content/docs/bff/architecture/ui-hosting.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,8 @@ a number of [deployment options](https://angular.io/guide/deployment) that allow
102102
assets.
103103

104104
The added complexity of this technique is justified when there is a requirement to host the front end on a different
105-
site (typically a CDN) from the BFF.
105+
site (typically a CDN) from the BFF.
106+
107+
:::note
108+
BFF V4 has built-in support for proxying the index.html from a CDN.
109+
:::

src/content/docs/bff/extensibility/http-forwarder.md

Lines changed: 88 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ You can customize the HTTP forwarder behavior in two ways
1717
* provide a customized HTTP client for outgoing calls
1818
* provide custom request/response transformation
1919

20-
### Custom HTTP clients
20+
## Custom HTTP Clients
21+
2122
By default, Duende.BFF will create and cache an HTTP client per configured route or local path.
2223

2324
This invoker is set up like this:
@@ -32,64 +33,112 @@ var client = new HttpMessageInvoker(new SocketsHttpHandler
3233
});
3334
```
3435

35-
If you want to customize the HTTP client for specific paths, you can either implement the *IHttpMessageInvokerFactory* interface or derive from the *DefaultHttpMessageInvokerFactory*, e.g.:
36+
If you want to customize the HTTP client you can implement the `IForwarderHttpClientFactory` interface, e.g.:
3637

3738
```cs
38-
public class MyInvokerFactory : DefaultHttpMessageInvokerFactory
39+
public class MyInvokerFactory : IForwarderHttpClientFactory
3940
{
40-
public override HttpMessageInvoker CreateClient(string localPath)
41+
public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context)
4142
{
42-
if (localPath == "/foo")
43+
return Clients.GetOrAdd(localPath, (key) =>
4344
{
44-
return Clients.GetOrAdd(localPath, (key) =>
45+
return new HttpMessageInvoker(new SocketsHttpHandler
4546
{
46-
return new HttpMessageInvoker(new SocketsHttpHandler
47-
{
48-
// this API needs a proxy
49-
UseProxy = true,
50-
Proxy = new WebProxy("https://myproxy"),
51-
52-
AllowAutoRedirect = false,
53-
AutomaticDecompression = DecompressionMethods.None,
54-
UseCookies = false
55-
});
47+
// this API needs a proxy
48+
UseProxy = true,
49+
Proxy = new WebProxy("https://myproxy"),
50+
51+
AllowAutoRedirect = false,
52+
AutomaticDecompression = DecompressionMethods.None,
53+
UseCookies = false
5654
});
57-
}
58-
59-
return base.CreateClient(localPath);
55+
});
6056
}
6157
}
6258
```
6359

6460
...and override our registration:
6561

6662
```cs
67-
services.AddSingleton<IHttpMessageInvokerFactory, MyInvokerFactory>();
63+
services.AddSingleton<IForwarderHttpClientFactory, MyInvokerFactory>();
6864
```
6965

70-
### Custom Transformations
71-
In the standard configuration, BFF uses the YARP default behavior for forwarding HTTP requests. In addition, we
66+
## Custom Transformations When Using Direct Forwarding
7267

73-
* remove the sensitive session cookie
74-
* add the current access token
68+
The method MapRemoteBffApiEndpoint uses default transformations that:
69+
* removes the cookie header from the forwarded request
70+
* removes local path from the forwarded request
71+
* Adds the access token to the original request
7572

76-
If you want to modify this behavior you can either implement *IHttpTransformerFactory* from scratch:
73+
If you wish to change or extend this behavior, you can do this for a single mapped endpoint
74+
or for all mapped API endpoints.
7775

78-
```cs
79-
public interface IHttpTransformerFactory
76+
### Changing The Transformer For A Single Mapped Endpoint
77+
78+
This code block shows an example how of you can extend the default transformers with an additional custom
79+
transform.
80+
81+
```csharp
82+
app.MapRemoteBffApiEndpoint("/local", new Uri("https://target/"), context => {
83+
84+
// If you want to extend the existing behavior, then you must call the default builder:
85+
DefaultBffYarpTransformerBuilders.DirectProxyWithAccessToken("/local", context);
86+
87+
// You can also add custom transformers, such as this one that adds an additional header
88+
context.AddRequestHeader("custom", "with value");
89+
90+
});
91+
```
92+
93+
The default transform builder performs these transforms:
94+
95+
```csharp
96+
context.AddRequestHeaderRemove("Cookie");
97+
context.AddPathRemovePrefix(localPath);
98+
context.AddBffAccessToken(localPath);
99+
```
100+
101+
For more information, also see the [YARP documentation on transforms](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/yarp/transforms?view=aspnetcore-9.0)
102+
103+
### Changing The Default Transformer
104+
105+
You can change the default transformer builder delegate by registering one in the services collection:
106+
107+
```csharp
108+
BffYarpTransformBuilder builder = (localPath, context) => {
109+
110+
// If you want to extend the existing behavior, then you must call the default builder:
111+
DefaultBffYarpTransformerBuilders.DirectProxyWithAccessToken(localpath, context);
112+
113+
// You can also add custom transformers, such as this one that adds an additional header
114+
context.AddResponseHeader("added-by-custom-default-transform", "some-value");
115+
116+
};
117+
118+
services.AddSingleton<BffYarpTransformBuilder>(builder);
119+
```
120+
121+
## Changing The Forwarder Request Configuration
122+
123+
You an also modify the forwarder request configuration, either globally or per mapped path.
124+
This can be useful if you want to tweak things like activity timeouts.
125+
126+
```csharp
127+
// Register a forwarder config globally:
128+
services.AddSingleton(new ForwarderRequestConfig()
80129
{
81-
/// <summary>
82-
/// Creates a HTTP transformer based on the local path
83-
/// </summary>
84-
/// <param name="localPath">Local path the remote API is mapped to</param>
85-
/// <param name="accessToken">The access token to attach to the request (if present)</param>
86-
/// <returns></returns>
87-
HttpTransformer CreateTransformer(string localPath, string accessToken = null);
88-
}
130+
ActivityTimeout = TimeSpan.FromMilliseconds(100)
131+
});
132+
133+
// Or modify one on a per mapped route basis:
134+
app.MapRemoteBffApiEndpoint("/local", new Uri("https://target/"),
135+
requestConfig: new ForwarderRequestConfig()
136+
{
137+
// 100 ms timeout, which is not too short that the normal process might fail,
138+
// but not too long that the test will take forever
139+
ActivityTimeout = TimeSpan.FromMilliseconds(100)
140+
});
89141
```
90142

91-
...or derive from the *DefaultHttpTransformerFactory*.
92143

93-
:::note
94-
The transformations are based on YARP's transform library and are extensible. See [here](https://microsoft.github.io/reverse-proxy/articles/transforms.html) for a full list of built-in transforms.
95-
:::
144+

0 commit comments

Comments
 (0)