diff --git a/astro/src/content/docs/bff/extensibility/tokens.md b/astro/src/content/docs/bff/extensibility/tokens.md index e71d523cc..1025013ea 100644 --- a/astro/src/content/docs/bff/extensibility/tokens.md +++ b/astro/src/content/docs/bff/extensibility/tokens.md @@ -126,4 +126,21 @@ app.MapRemoteBffApiEndpoint( .WithAccessTokenRetriever(); ``` -The `GetAccessTokenAsync` method will be invoked on every call to APIs that use the access token retriever. If retrieving the token is an expensive operation, you may need to cache it. It is up to your retriever code to perform caching. \ No newline at end of file +Custom access token retrievers can also be used with [YARP routes and clusters](/bff/fundamentals/apis/yarp.md#custom-access-token-retriever). +Use the `WithAccessTokenRetriever()` extension method on a `RouteConfig` or `ClusterConfig`, +or set the `Duende.Bff.Yarp.AccessTokenRetriever` metadata key in JSON configuration: + +```csharp +// YARP route with custom retriever +new RouteConfig() +{ + RouteId = "impersonation", + ClusterId = "cluster1", + Match = new RouteMatch { Path = "/api/impersonation/{**catch-all}" } +}.WithAccessToken(RequiredTokenType.User) + .WithAccessTokenRetriever() +``` + +The `GetAccessTokenAsync` method will be invoked on every call to APIs that use the access token retriever. + If retrieving the token is an expensive operation, you may need to cache it. It is up to your retriever code + to perform caching. \ No newline at end of file diff --git a/astro/src/content/docs/bff/fundamentals/apis/yarp.md b/astro/src/content/docs/bff/fundamentals/apis/yarp.md index b9b664a3c..c70061817 100644 --- a/astro/src/content/docs/bff/fundamentals/apis/yarp.md +++ b/astro/src/content/docs/bff/fundamentals/apis/yarp.md @@ -281,4 +281,92 @@ app.MapReverseProxy(proxyApp => { proxyApp.UseAntiforgeryCheck(); }); -``` \ No newline at end of file +``` + +## Custom Access Token Retriever + +You can specify a custom [`IAccessTokenRetriever`](/bff/extensibility/tokens.md#per-route-customized-token-retrieval) on +YARP routes and clusters. This allows you to customize how access tokens are obtained for proxied requests — for +example, to perform token exchange for delegation or impersonation scenarios. + +### Code Configuration + +A custom retriever can be set at the **route level** or the **cluster level**. Route-level retrievers take precedence +over cluster-level retrievers. + +Use the `WithAccessTokenRetriever()` extension method on a `RouteConfig`: + +```csharp +// Route-level retriever +new RouteConfig() +{ + RouteId = "impersonation", + ClusterId = "cluster1", + + Match = new RouteMatch + { + Path = "/api/impersonation/{**catch-all}" + } +}.WithAccessToken(RequiredTokenType.User) + .WithAccessTokenRetriever() + .WithAntiforgeryCheck() +``` + +Or `ClusterConfig`: + +```csharp +// Cluster-level retriever (applies to all routes using this cluster) +new ClusterConfig() +{ + ClusterId = "cluster-with-impersonation", + Destinations = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "destination1", new() { Address = "https://api.example.com" } }, + } +}.WithAccessTokenRetriever() +``` + +### JSON Configuration + +Use the `Duende.Bff.Yarp.AccessTokenRetriever` metadata key with an assembly-qualified type name: + +```json +{ + "ReverseProxy": { + "Routes": { + "impersonation": { + "ClusterId": "cluster1", + "Match": { + "Path": "/api/impersonation/{**catch-all}" + }, + "Metadata": { + "Duende.Bff.Yarp.TokenType": "User", + "Duende.Bff.Yarp.AntiforgeryCheck": "true", + "Duende.Bff.Yarp.AccessTokenRetriever": "MyApp.ImpersonationAccessTokenRetriever, MyApp" + } + } + }, + "Clusters": { + "cluster-with-impersonation": { + "Destinations": { + "destination1": { + "Address": "https://api.example.com" + } + }, + "Metadata": { + "Duende.Bff.Yarp.AccessTokenRetriever": "MyApp.ImpersonationAccessTokenRetriever, MyApp" + } + } + } + } +} +``` + +### Precedence + +When a retriever is specified on both the route and the cluster, the **route-level retriever takes precedence**. This +allows you to set a default retriever on a cluster and override it for specific routes. + +:::note +The custom retriever type must implement `IAccessTokenRetriever` and be registered in the service collection. +::: \ No newline at end of file