Skip to content

Commit 28ede18

Browse files
last BFF v3 fixes to docs (#620)
* last fixes to docs * Update BFF/v3/docs/content/fundamentals/blazor/rendering-modes.md Co-authored-by: Maarten Balliauw <maarten.balliauw@duendesoftware.com> * Update BFF/v3/docs/content/fundamentals/blazor/rendering-modes.md Co-authored-by: Maarten Balliauw <maarten.balliauw@duendesoftware.com> --------- Co-authored-by: Maarten Balliauw <maarten.balliauw@duendesoftware.com>
1 parent ab57a09 commit 28ede18

5 files changed

Lines changed: 996 additions & 11 deletions

File tree

BFF/v3/docs/content/fundamentals/blazor/_index.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@ Blazor is very flexible in how it renders applications (and even individual comp
2929

3030
These rendering modes are very powerful, but also add additional complexity when it comes to authentication and authorization. Any code that executes on the server can directly access local resources, such has a database, but code that executes on the client needs to through a local http endpoint (that requires authentication). Accessing external API's is also different between server and client, where the client needs to go through a proxy which performs a token exchange.
3131

32+
For more information on this, see [rendering-modes]({{< ref "/fundamentals/blazor/rendering-modes" >}})
3233

33-
### Authentication State Providers
34+
### Authentication State
35+
The **AuthenticationState ** is contains information about the currently logged in user. This is partly populated from information from the user, but is also enriched with several management claims, such as the Logout URL.
3436

35-
Blazor uses AuthenticationStateProviders to make authentication state available to components. Depending on if a component is currently executing on the server or on the client, the implementation needs to be different. For this reason, the Duende BFF Security Framework contains two state providers: **BffServerAuthenticationStateProvider** and **BffClientAuthenticationStateProvider**.
36-
37-
On the server, the authentication state is already mostly managed by the framework. On the client, that's not the case. The **BffClientAuthenticationStateProvider** will poll the server to update the client on the latest authentication state, such as the user's claims. This also notifies the front-end if the session is terminated on the server.
38-
39-
Lastly, the authentications state providers also add the [management]({{< ref "/samples/bff" >}}) to the user's claims.
37+
Blazor uses AuthenticationStateProviders to make authentication state available to components. On the server, the authentication state is already mostly managed by the authentication framework. However, the BFF will add the Logout url to the claims using the **AddServerManagementClaimsTransform**. On the client, there are some other claims that might be useful. The **BffClientAuthenticationStateProvider** will poll the server to update the client on the latest authentication state, such as the user's claims. This also notifies the front-end if the session is terminated on the server.
4038

4139
### Server Side Token Store
4240

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
title: "Blazor Rendering modes"
3+
date: 2020-09-10T08:22:12+02:00
4+
weight: 10
5+
---
6+
7+
Blazor supports [several rendering](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-9.0#render-modes) modes:
8+
* **Static Server** - Static server-side rendering (static SSR)
9+
* **Interactive Server** - Interactive server-side rendering (interactive SSR) using Blazor Server.
10+
* **Interactive WebAssembly** - Client-side rendering (CSR) using Blazor WebAssembly.
11+
* **Interactive Auto** - Interactive SSR using Blazor Server initially and then CSR on subsequent visits after the Blazor bundle is downloaded.
12+
13+
While these options give a lot of flexibility on how sites and components are rendered, it also provides some complexity.
14+
15+
It's important to understand that, if you use a rendering mode that uses WebAssembly (so InteractiveWebAssembly or Auto), that you're effectively building two applications. One is a server process that renders HTML, the other is a WASM application that runs in the browser.
16+
17+
If you have a component that's rendered both on the server AND on the client, then you effectively need to make sure that all the services it requires are available both on the server AND on the client.
18+
19+
## Fetching data from local APIs
20+
21+
If your BFF application can directly access data (for example from a database), then you have to decide where this information is rendered.
22+
23+
For server side rendering, you'll typically abstract your data access logic into a separate class (such as a repository or a query object) and inject this into your component for rendering.
24+
25+
For web assembly rendering, you'll need to make the data available via a web service on the server. Then on the client, you'll need a configured HTTP client that accesses this information securely.
26+
27+
When using auto-rendering mode, you'll need to make sure that the component get's a different 'data access' component for server rendering vs client rendering. Consider the following diagram:
28+
29+
![local api's](../../../images/bff_blazor_local_api.svg)
30+
31+
In this diagram, you'll see the example **IDataAccessor** that has two implementations. One that accesses the data via a HTTP client (for use in WASM) and one that directly accesses the data.
32+
33+
The data is also exposed (and secured by the BFF) via a local api.
34+
35+
Below is an example of registering an abstraction
36+
37+
``` csharp
38+
// Setup on the server
39+
40+
// Register the server implementation for accessing some data
41+
builder.Services.AddSingleton<IDataAccessor, ServerDataAccessor>();
42+
43+
// Register an api that will access the data
44+
app.MapGet("/some_data", async (IDataAccessor dataAccessor) => await dataAccessor.GetData())
45+
.RequireAuthorization()
46+
.AsBffApiEndpoint();
47+
48+
// Create a class that would actually get the data from the database
49+
internal class ServerWeatherClient() : IDataAccessor
50+
{
51+
public Task<Data[]> GetData()
52+
{
53+
// get the actual data from the database
54+
}
55+
}
56+
57+
```
58+
59+
``` csharp
60+
// setup on the client
61+
62+
// Register a http client that can access the data via a local api.
63+
builder.Services.AddLocalApiHttpClient<DataAccessHttpClient>();
64+
65+
// Register an adapter that would abstract between the data accessor and the http client.
66+
builder.Services.AddSingleton<IDataAccessor>(sp => sp.GetRequiredService<HttpClientDataAccessor>());
67+
68+
internal class HttpClientDataAccessor(HttpClient client) : IDataAccessor
69+
{
70+
public async Task<Data[]> GetSomeData() => await client.GetFromJsonAsync<Data[]>("/some_data")
71+
?? throw new JsonException("Failed to deserialize");
72+
}
73+
74+
```
75+
76+
## Fetching data from remote APIs
77+
78+
If your BFF needs to secure access to remote api's, then your components can both directly use a (typed) **HttpClient**. How this HttpClient is configured is quite different on the client vs the server though.
79+
80+
81+
* On the **Client**, the http client needs to be secured with the authentication cookie and CORS protection headers. This
82+
then calls the http endpoint on the server.
83+
84+
* On the **Server**, you'd need to expose the proxied http endpoint. This then uses a http client that's configured to send access tokens. These may or may not contain a user token.
85+
86+
This diagram shows this in more detail:
87+
88+
![remote api's](../../../images/bff_blazor_remote_api.svg)
89+
90+
``` csharp
91+
// setup on the server
92+
93+
app.MapRemoteBffApiEndpoint("/remote-apis/user-token", "https://localhost:5010")
94+
95+
builder.Services.AddUserAccessTokenHttpClient("callApi",
96+
configureClient: client => client.BaseAddress = new Uri("https://localhost:5010/"));
97+
98+
99+
```
100+
101+
``` csharp
102+
// setup on the client
103+
builder.services.AddRemoteApiHttpClient("callApi");
104+
```

BFF/v3/docs/content/fundamentals/options.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,9 +168,4 @@ builder.Services.AddBffBlazorClient(opt =>
168168
* ***WebAssemblyStateProviderPollingInterval***
169169
The delay, in milliseconds, between polling requests by the
170170
BffClientAuthenticationStateProvider to the /bff/user endpoint. Defaults to 5000
171-
ms.
172-
173-
* ***ServerStateProviderPollingInterval***
174-
The delay, in milliseconds, between polling requests by the
175-
BffServerAuthenticationStateProvider to the /bff/user endpoint. Defaults to 5000
176171
ms.

0 commit comments

Comments
 (0)