Skip to content

Commit 81a78bc

Browse files
Erwinvandervalkmaartenba
authored andcommitted
BFF v4 architecture and fundamentals
1 parent 352339f commit 81a78bc

File tree

5 files changed

+231
-30
lines changed

5 files changed

+231
-30
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,39 @@ After your application's logic is executed, there are two middlewares registered
6262

6363
6. `ProxyIndexMiddleware` - If configured, this proxy 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`. Please note then you're responsible for manually adding 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+
// Only add this if you want to proxy to remote api's.
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 it's own scheme, and potentially it's 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 it's own OpenID Connect and Cookie settings.
65100

src/content/docs/bff/fundamentals/multi-frontend/configuration.mdx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
---
2-
title: "BFF Configuration"
2+
title: "BFF multi-frontend Configuration"
33
description: Documentation for managing BFF configuration
44
sidebar:
55
order: 3
66
label: "Configuration"
77

88
---
9-
It's possible to configure the BFF via IConfiguration.
9+
It's possible to configure frontends for the BFF via IConfiguration. This enables dynamic loading / changing of frontends,
10+
including their OpenID Connect configuration and BFF Configuration.
1011

1112
```csharp {6}
1213

src/content/docs/bff/fundamentals/multi-frontend/index.mdx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,74 @@ To overcome this issue, a single BFF instance can support multiple frontends. Ea
2525
Adding additional frontends to the BFF has very little impact on the performance on the BFF itself, but keep in mind
2626
that the traffic for all the frontends is proxied through the bff.
2727

28+
## Authentication configuration
29+
30+
31+
When you use multiple frontends, you can't rely on [manual authentication configuration](../fundamentals/session/handlers.mdx#manually-configuring-authentication).
32+
This is because each frontend requires it's own scheme, and potentially it's own OpenID Connect and Cookie configuration.
33+
34+
Instead, you should rely on [automatic authentication configuration](../fundamentals/session/handlers.mdx#automatic-authentication-configuration).
35+
36+
Below is an example on how to configure multiple frontends.
37+
38+
```csharp
39+
var bffBuilder = builder.Services
40+
.AddBff();
41+
42+
bffBuilder
43+
.ConfigureOpenIdConnect(options =>
44+
{
45+
// These are the default values for all frontends.
46+
options.Authority = "https://demo.duendesoftware.com";
47+
options.ClientId = "interactive.confidential";
48+
options.ClientSecret = "secret";
49+
options.ResponseType = "code";
50+
options.ResponseMode = "query";
51+
52+
options.GetClaimsFromUserInfoEndpoint = true;
53+
options.SaveTokens = true;
54+
options.MapInboundClaims = false;
55+
56+
options.Scope.Clear();
57+
options.Scope.Add("openid");
58+
options.Scope.Add("profile");
59+
options.Scope.Add("api");
60+
options.Scope.Add("offline_access");
61+
62+
options.TokenValidationParameters.NameClaimType = "name";
63+
options.TokenValidationParameters.RoleClaimType = "role";
64+
});
65+
66+
.AddFrontends(
67+
68+
// This frontend will use the default authentication options
69+
new BffFrontend(BffFrontendName.Parse("default-frontend")),
70+
71+
// This frontend uses most of the same authentication options,
72+
new BffFrontend(BffFrontendName.Parse("with-path"))
73+
.MappedToPath(LocalPath.Parse("/with-path")),
74+
.WithOpenIdConnectOptions(opt =>
75+
{
76+
// but overrides the clientid and client secret.
77+
opt.ClientId = "different-client-id";
78+
opt.ClientSecret = "different secret";
79+
})
80+
.WithCookieOptions(opt =>
81+
{
82+
// and overrides the cookie options to use 'lax' cookies.
83+
opt.Cookie.SameSite = SameSiteMode.Lax;
84+
});
85+
86+
```
87+
88+
The order in which configuration is applied is
89+
1. programmatic default options (if any)
90+
2. default options from configuration (if any)
91+
3. frontend specific options (if any)
92+
93+
Each frontend can have custom OpenID Connect configuration and Cookie Configuration. This can both be configured programmatically
94+
as via [Configuration](./configuration.mdx).
95+
2896
## Frontend selection
2997

3098
Each request to a frontend has to be uniquely defined by either it's path, it's origin or a combination of the two.

src/content/docs/bff/fundamentals/session/handlers.md renamed to src/content/docs/bff/fundamentals/session/handlers.mdx

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,58 @@ redirect_from:
1111
- /identityserver/v6/bff/session/handlers/
1212
- /identityserver/v7/bff/session/handlers/
1313
---
14+
import { Badge } from "@astrojs/starlight/components";
15+
import { Code } from "@astrojs/starlight/components";
16+
import { Tabs, TabItem } from "@astrojs/starlight/components";
17+
18+
To configure authentication in the BFF, you'll need to configure both the OpenID Connect
19+
login flow and the cookie handlers.
20+
21+
## Automatic authentication configuration <Badge text="V4" />
22+
23+
In V4, a simplified mechanism for wiring up authentication has been introduced. The main purpose for the BFF
24+
is to handle the OpenID Connect login flow and to protect the api's using Cookies. In V3, you explicitly had
25+
to configure the aspnet core authentication system to enable this, but in V4, this is now simplified.
26+
27+
A call `BffBuilder.ConfigureOpenIdConnect()` will make sure that:
28+
1. The authentication pipeline is configured with the appropriate authentication schemes.
29+
2. The OpenID Connect pipeline is configured with default values.
30+
3. The CookieHandler is configured using recommended practices. This can be tweaked by calling `BffBuilder.ConfigureCookies()`
31+
32+
Below is an example on how to configure the BFF's authentication pipeline.
33+
34+
```csharp
35+
36+
services.AddBff()
37+
.ConfigureOpenIdConnect(options =>
38+
{
39+
options.Authority = "https://demo.duendesoftware.com";
40+
options.ClientId = "interactive.confidential";
41+
options.ClientSecret = "secret";
42+
options.ResponseType = "code";
43+
options.ResponseMode = "query";
44+
45+
options.GetClaimsFromUserInfoEndpoint = true;
46+
options.SaveTokens = true;
47+
options.MapInboundClaims = false;
48+
49+
options.Scope.Clear();
50+
options.Scope.Add("openid");
51+
options.Scope.Add("profile");
52+
options.Scope.Add("api");
53+
options.Scope.Add("offline_access");
54+
55+
options.TokenValidationParameters.NameClaimType = "name";
56+
options.TokenValidationParameters.RoleClaimType = "role";
57+
});
58+
```
59+
60+
Each frontend can have custom OpenID Connect configuration and Cookie Configuration. This can both be configured programmatically
61+
as via [Configuration](../multi-frontend/configuration.mdx).
62+
63+
64+
## Manually configuring authentication
65+
1466

1567
You typically use the following two ASP.NET Core authentication handlers to implement remote authentication:
1668

@@ -43,7 +95,7 @@ builder.Services.AddAuthentication(options =>
4395

4496
Now let's look at some more details!
4597

46-
## The OpenID Connect Authentication Handler
98+
### The OpenID Connect Authentication Handler
4799

48100
The OpenID Connect (OIDC) handler connects the application to the authentication / access token system.
49101

@@ -93,7 +145,7 @@ builder.Services.AddAuthentication().AddOpenIdConnect("oidc", options =>
93145
```
94146
The OIDC handler will use the default sign-in handler (the cookie handler) to establish a session after successful validation of the OIDC response.
95147

96-
## The Cookie Handler
148+
### The Cookie Handler
97149

98150
The cookie handler is responsible for establishing the session and manage authentication session related data.
99151

src/content/docs/bff/fundamentals/session/server-side-sessions.md renamed to src/content/docs/bff/fundamentals/session/server-side-sessions.mdx

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ redirect_from:
1010
- /identityserver/v6/bff/session/server_side_sessions/
1111
- /identityserver/v7/bff/session/server_side_sessions/
1212
---
13+
import { Badge } from "@astrojs/starlight/components";
14+
import { Code } from "@astrojs/starlight/components";
15+
import { Tabs, TabItem } from "@astrojs/starlight/components";
1316

1417
By default, ASP.NET Core's cookie handler will store all user session data in a protected cookie. This works very well unless cookie size or revocation becomes an issue.
1518

@@ -47,34 +50,76 @@ builder.Services.AddBff()
4750
Most datastores that you might use with Entity Framework use a schema to define the structure of their data. *Duende.BFF.EntityFramework* doesn't make any assumptions about the underlying datastore, how (or indeed even if) it defines its schema, or how schema changes are managed by your organization. For these reasons, Duende does not directly support database creation, schema changes, or data migration by publishing database scripts. You are expected to manage your database in the way your organization sees fit. Using EF migrations is one possible approach to that, which Duende facilitates by publishing entity classes in each version of *Duende.BFF.EntityFramework*. An example project that uses those entities to create migrations is [here](https://github.com/DuendeSoftware/products/tree/main/bff/migrations/UserSessionDb).
4851

4952
## Session Store Cleanup
50-
5153
Added in v1.2.0.
5254

5355
Abandoned sessions will remain in the store unless something removes the stale entries.
54-
If you wish to have such sessions cleaned up periodically, then you can configure the *EnableSessionCleanup* and *SessionCleanupInterval* options:
5556

56-
```csharp
57-
builder.Services.AddBff(options => {
58-
options.EnableSessionCleanup = true;
59-
options.SessionCleanupInterval = TimeSpan.FromMinutes(5);
60-
})
61-
.AddServerSideSessions();
62-
```
57+
{/* prettier-ignore */}
58+
<Tabs syncKey="bffVersion">
59+
{/* prettier-ignore */}
60+
<TabItem label="V4">
61+
62+
If you wish to have such sessions cleaned up periodically, then you can add the session cleanup host and configure the *SessionCleanupInterval* options:
63+
64+
```csharp
65+
builder.Services.AddBff(options => {
66+
options.SessionCleanupInterval = TimeSpan.FromMinutes(5);
67+
})
68+
.AddServerSideSessions();
69+
```
70+
71+
This requires an implementation of [*IUserSessionStoreCleanup*](/bff/extensibility/sessions#user-session-store-cleanup) in the ASP.NET Core service provider.
72+
73+
If using Entity Framework Core, then the *IUserSessionStoreCleanup* implementation is provided for you when you use *AddEntityFrameworkServerSideSessions*.
74+
You can then add the `SessionCleanupBackgroundProcess`:
75+
76+
```csharp
77+
var cn = _configuration.GetConnectionString("db");
78+
79+
builder.Services.AddBff()
80+
.AddEntityFrameworkServerSideSessions(options =>
81+
{
82+
options.UseSqlServer(cn);
83+
})
84+
.AddSessionCleanupBackgroundProcess();
85+
```
86+
:::note
87+
In V4, we changed how you enable session cleanup. We no longer automatically register the session cleanup hosted service. This has to be done manually.
88+
In a loadbalanced environment, you can choose to run the cleanup job on all instances the BFF. However, you can also decide to
89+
spin up a separate host that's responsible for background jobs such as this cleanup job.
90+
:::
91+
92+
</TabItem>
93+
<TabItem label="V3">
94+
95+
If you wish to have such sessions cleaned up periodically, then you can configure the *EnableSessionCleanup* and *SessionCleanupInterval* options:
96+
97+
```csharp
98+
builder.Services.AddBff(options => {
99+
options.EnableSessionCleanup = true;
100+
options.SessionCleanupInterval = TimeSpan.FromMinutes(5);
101+
})
102+
.AddServerSideSessions();
103+
```
104+
105+
This requires an implementation of [*IUserSessionStoreCleanup*](/bff/extensibility/sessions#user-session-store-cleanup) in the ASP.NET Core service provider.
106+
107+
If using Entity Framework Core, then the *IUserSessionStoreCleanup* implementation is provided for you when you use *AddEntityFrameworkServerSideSessions*.
108+
Just enable session cleanup:
109+
110+
```csharp
111+
var cn = _configuration.GetConnectionString("db");
112+
113+
builder.Services.AddBff(options =>
114+
{
115+
options.EnableSessionCleanup = true;
116+
})
117+
.AddEntityFrameworkServerSideSessions(options =>
118+
{
119+
options.UseSqlServer(cn);
120+
});
121+
```
122+
123+
</TabItem>
124+
</Tabs>
63125

64-
This requires an implementation of [*IUserSessionStoreCleanup*](/bff/extensibility/sessions#user-session-store-cleanup) in the ASP.NET Core service provider.
65-
66-
If using Entity Framework Core, then the *IUserSessionStoreCleanup* implementation is provided for you when you use *AddEntityFrameworkServerSideSessions*.
67-
Just enable session cleanup:
68-
69-
```csharp
70-
var cn = _configuration.GetConnectionString("db");
71-
72-
builder.Services.AddBff(options =>
73-
{
74-
options.EnableSessionCleanup = true;
75-
})
76-
.AddEntityFrameworkServerSideSessions(options =>
77-
{
78-
options.UseSqlServer(cn);
79-
});
80-
```

0 commit comments

Comments
 (0)