Skip to content

Commit fe72572

Browse files
Erwinvandervalkmaartenba
authored andcommitted
BFF v4 endpoints
1 parent 81a78bc commit fe72572

File tree

16 files changed

+150
-132
lines changed

16 files changed

+150
-132
lines changed

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

Lines changed: 91 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ 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
2121
By default, Duende.BFF will create and cache an HTTP client per configured route or local path.
2222

2323
This invoker is set up like this:
@@ -32,64 +32,116 @@ var client = new HttpMessageInvoker(new SocketsHttpHandler
3232
});
3333
```
3434

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.:
35+
If you want to customize the HTTP client you can either implement the *IForwarderHttpClientFactory* interface, e.g.:
3636

3737
```cs
38-
public class MyInvokerFactory : DefaultHttpMessageInvokerFactory
38+
public class MyInvokerFactory : IForwarderHttpClientFactory
3939
{
40-
public override HttpMessageInvoker CreateClient(string localPath)
40+
public public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context)
4141
{
42-
if (localPath == "/foo")
42+
return Clients.GetOrAdd(localPath, (key) =>
4343
{
44-
return Clients.GetOrAdd(localPath, (key) =>
44+
return new HttpMessageInvoker(new SocketsHttpHandler
4545
{
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-
});
46+
// this API needs a proxy
47+
UseProxy = true,
48+
Proxy = new WebProxy("https://myproxy"),
49+
50+
AllowAutoRedirect = false,
51+
AutomaticDecompression = DecompressionMethods.None,
52+
UseCookies = false
5653
});
57-
}
58-
59-
return base.CreateClient(localPath);
54+
});
6055
}
6156
}
6257
```
6358

6459
...and override our registration:
6560

6661
```cs
67-
services.AddSingleton<IHttpMessageInvokerFactory, MyInvokerFactory>();
62+
services.AddSingleton<IForwarderHttpClientFactory, MyInvokerFactory>();
6863
```
6964

70-
### Custom Transformations
71-
In the standard configuration, BFF uses the YARP default behavior for forwarding HTTP requests. In addition, we
65+
## Custom transformations when using Direct Forwarding
7266

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

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

78-
```cs
79-
public interface IHttpTransformerFactory
75+
### Changing the transformer for a single mapped endpoint
76+
77+
This code block shows an example how you can extend the default transformers with an additional custom
78+
transform.
79+
80+
```csharp
81+
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+
94+
The default transformbuilder performs these transforms:
95+
96+
```csharp
97+
context.AddRequestHeaderRemove("Cookie");
98+
context.AddPathRemovePrefix(localPath);
99+
context.AddBffAccessToken(localPath);
100+
```
101+
102+
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)
103+
104+
### Changing the default transformer.
105+
106+
You can change the default transformer builder delegate by registering one in the services collection:
107+
108+
```csharp
109+
BffYarpTransformBuilder builder = (localPath, context) => {
110+
111+
// If you want to extend the existing behavior, then you must call the default builder:
112+
DefaultBffYarpTransformerBuilders.DirectProxyWithAccessToken(localpath, context);
113+
114+
//You can also add custom transformers, such as this one that adds an additional header
115+
context.AddResponseHeader("added-by-custom-default-transform", "some-value");
116+
117+
};
118+
119+
services.AddSingleton<BffYarpTransformBuilder>(builder);
120+
```
121+
122+
## Changing the Forwarder Request Config
123+
124+
You an also modify the forwarder request config, either globally or per mapped path.
125+
This can be useful if you want to tweak things like activity timeouts.
126+
127+
```csharp
128+
129+
// Register a forwarder config globally.
130+
services.AddSingleton(new ForwarderRequestConfig()
80131
{
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-
}
132+
ActivityTimeout = TimeSpan.FromMilliseconds(100)
133+
});
134+
135+
// Or modify one on a per mapped route basis:
136+
app.MapRemoteBffApiEndpoint("/local", new Uri("https://target/"),
137+
requestConfig: new ForwarderRequestConfig()
138+
{
139+
// 100 ms timeout, which is not too short that the normal process might fail,
140+
// but not too long that the test will take forever
141+
ActivityTimeout = TimeSpan.FromMilliseconds(100)
142+
});
143+
89144
```
90145

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

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-
:::
147+

src/content/docs/bff/extensibility/management/back-channel-logout.md

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,68 +12,23 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/back-channel-logout/
1313
---
1414

15-
The back-channel logout endpoint has several extensibility points organized into two interfaces and their default implementations. The *IBackChannelLogoutService* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic or to change how it validates incoming requests. When the back-channel logout endpoint receives a valid request, it revokes sessions using the *ISessionRevocationService*.
15+
The back-channel logout endpoint has several extensibility points organized into two interfaces. The *IBackChannelLogoutEndpoint* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic or to change how it validates incoming requests. When the back-channel logout endpoint receives a valid request, it revokes sessions using the *ISessionRevocationService*.
1616

1717
## Request Processing
18-
You can add custom logic to the endpoint by implementing the *IBackChannelLogoutService* or by extending its default implementation (*Duende.Bff.DefaultBackChannelLogoutService*). In most cases, extending the default implementation is preferred, as it has several virtual methods that can be overridden to customize particular aspects of how the request is processed.
18+
You can add custom logic to the endpoint by implementing the *IBackChannelLogoutEndpoint* .
1919

2020
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
2121

2222
```csharp
23-
public class CustomizedBackChannelLogoutService : DefaultBackChannelLogoutService
23+
public class CustomizedBackChannelLogoutService : IBackChannelLogoutEndpoint
2424
{
25-
public override Task ProcessRequestAsync(HttpContext context)
25+
public override Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2626
{
2727
// Custom logic here
28-
29-
return base.ProcessRequestAsync(context);
30-
}
31-
}
32-
```
33-
34-
## Validation
35-
36-
Validation of the incoming request can be customized by overriding one of several virtual methods in the *DefaultBackChannelLogoutService*. *GetTokenValidationParameters* allows you to specify the *[TokenValidationParameters](https://learn.microsoft.com/en-us/dotnet/API/microsoft.identitymodel.tokens.tokenvalidationparameters?view=azure-dotnet)* used to validate the incoming logout token. The default implementation creates token validation parameters based on the authentication scheme's configuration. Your override could begin by calling the base method and then make changes to those parameters or completely customize how token validation parameters are created. For example:
37-
38-
```csharp
39-
public class CustomizedBackChannelLogoutService : DefaultBackChannelLogoutService
40-
{
41-
protected override async Task<TokenValidationParameters> GetTokenValidationParameters()
42-
{
43-
var tokenValidationParams = await base.GetTokenValidationParameters();
44-
45-
// Set custom parameters here
46-
// For example, make clock skew more permissive than it is by default:
47-
tokenValidationParams.ClockSkew = TimeSpan.FromMinutes(15);
48-
49-
return tokenValidationParams;
5028
}
5129
}
5230
```
53-
If you need more control over the validation of the logout token, you can override *ValidateJwt*. The default implementation of *ValidateJwt* validates the token and produces a *ClaimsIdentity* using a *[JsonWebTokenHandler](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/ValidatingTokens)* and the token validation parameters returned from *GetTokenValidationParameters*. Your override could call the base method and then manipulate this *ClaimsIdentity* or add a completely custom method for producing the *ClaimsIdentity* from the logout token.
54-
55-
*ValidateLogoutTokenAsync* is the coarsest-grained validation method. It is responsible for validating the incoming logout token and determining if logout should proceed, based on claims in the token. It returns a *ClaimsIdentity* if logout should proceed or null if it should not. Your override could prevent logout in certain circumstances by returning null. For example:
56-
57-
```csharp
58-
public class CustomizedBackChannelLogoutService : DefaultBackChannelLogoutService
59-
{
60-
protected override async Task<ClaimsIdentity?> ValidateLogoutTokenAsync(string logoutToken)
61-
{
62-
var identity = await base.ValidateLogoutTokenAsync(logoutToken);
6331

64-
// Perform custom logic here
65-
// For example, prevent logout based on certain conditions
66-
if(identity?.FindFirst("sub")?.Value == "12345")
67-
{
68-
return null;
69-
}
70-
else
71-
{
72-
return identity;
73-
}
74-
}
75-
}
76-
```
7732

7833
## Session Revocation
7934
The back-channel logout service will call the registered session revocation service to revoke the user session when it receives a valid logout token. To customize the revocation process, implement the *ISessionRevocationService*.

src/content/docs/bff/extensibility/management/diagnostics.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,17 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/diagnostics/
1313
---
1414

15-
The BFF diagnostics endpoint can be customized by implementing the *IDiagnosticsService* or by extending *DefaultDiagnosticsService*, its default implementation.
15+
The BFF diagnostics endpoint can be customized by implementing the *IDiagnosticsEndpoint*.
1616

1717
## Request Processing
1818
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
1919

2020
For example, you could take whatever actions you need before normal processing of the request like this:
2121

2222
```csharp
23-
public override Task ProcessRequestAsync(HttpContext context)
23+
public Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2424
{
2525
// Custom logic here
2626
27-
return base.ProcessRequestAsync(context);
2827
}
2928
```

src/content/docs/bff/extensibility/management/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ builder.Services.AddTransient<IDiagnosticsService, DefaultDiagnosticsService>();
2727

2828
You can add your own implementation by overriding the default after calling *AddBff()*.
2929

30-
The management endpoint services all inherit from the *IBffEndpointService*, which provides a general-purpose mechanism to add custom logic to the endpoints.
30+
The management endpoint services all inherit from the *IBffEndpointEndpoint*, which provides a general-purpose mechanism to add custom logic to the endpoints.
3131

3232
```csharp
3333
public interface IBffEndpointService
3434
{
35-
Task ProcessRequestAsync(HttpContext context);
35+
Task ProcessRequestAsync(HttpContext context, CancellationToken ct);
3636
}
3737
```
3838

src/content/docs/bff/extensibility/management/login.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/login/
1313
---
1414

15-
The BFF login endpoint has extensibility points in two interfaces. The *ILoginService* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic. The *IReturnUrlValidator* ensures that the *returnUrl* parameter passed to the login endpoint is safe to use.
15+
The BFF login endpoint has extensibility points in two interfaces. The *ILoginEndpoint* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic. The *IReturnUrlValidator* ensures that the *returnUrl* parameter passed to the login endpoint is safe to use.
1616

1717
## Request Processing
1818
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
1919

2020
For example, you could take whatever actions you need before normal processing of the request like this:
2121

2222
```csharp
23-
public override Task ProcessRequestAsync(HttpContext context)
23+
public Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2424
{
2525
// Custom logic here
2626

src/content/docs/bff/extensibility/management/logout.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/logout/
1313
---
1414

15-
The BFF logout endpoint has extensibility points in two interfaces. The *ILogoutService* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic. The *IReturnUrlValidator* ensures that the *returnUrl* parameter passed to the logout endpoint is safe to use.
15+
The BFF logout endpoint has extensibility points in two interfaces. The *ILogoutEndpoint* is the top level abstraction that processes requests to the endpoint. This service can be used to add custom request processing logic. The *IReturnUrlValidator* ensures that the *returnUrl* parameter passed to the logout endpoint is safe to use.
1616

1717
## Request Processing
1818
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
1919

2020
For example, you could take whatever actions you need before normal processing of the request like this:
2121

2222
```csharp
23-
public override Task ProcessRequestAsync(HttpContext context)
23+
public Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2424
{
2525
// Custom logic here
2626

src/content/docs/bff/extensibility/management/silent-login-callback.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/silent-login-callback/
1313
---
1414

15-
The BFF silent login callback endpoint can be customized by implementing the *ISilentLoginCallbackService* or by extending *DefaultSilentLoginCallbackService*, its default implementation.
15+
The BFF silent login callback endpoint can be customized by implementing the *ISilentLoginCallbackEndpoint*.
1616

1717
## Request Processing
1818
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
1919

2020
For example, you could take whatever actions you need before normal processing of the request like this:
2121

2222
```csharp
23-
public override Task ProcessRequestAsync(HttpContext context)
23+
public Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2424
{
2525
// Custom logic here
2626

src/content/docs/bff/extensibility/management/silent-login.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ redirect_from:
1212
- /identityserver/v7/bff/extensibility/management/silent-login/
1313
---
1414

15-
The BFF silent login endpoint can be customized by implementing the *ISilentLoginService* or by extending *DefaultSilentLoginService*, its default implementation.
15+
The BFF silent login endpoint can be customized by implementing the *ISilentLoginEndpoint*.
1616

1717
## Request Processing
1818
*ProcessRequestAsync* is the top level function called in the endpoint service and can be used to add arbitrary logic to the endpoint.
1919

2020
For example, you could take whatever actions you need before normal processing of the request like this:
2121

2222
```csharp
23-
public override Task ProcessRequestAsync(HttpContext context)
23+
public Task ProcessRequestAsync(HttpContext context, CancellationToken ct)
2424
{
2525
// Custom logic here
2626

0 commit comments

Comments
 (0)