Token Management

Duende.BFF includes an automatic token management feature. This uses the access and refresh token stored in the authentication session to always provide a current access token for outgoing API calls.

For most scenarios, there is no additional configuration necessary. The token management will infer the configuration and token endpoint URL from the metadata of the OpenID Connect provider.

The easiest way to retrieve the current access token is to use an extension method on HttpContext:

    var token = await HttpContext.GetUserAccessTokenAsync();

You can then use the token to set it on an HttpClient instance:

    var client = new HttpClient();
    client.SetBearerToken(token);

We recommend to leverage the HttpClientFactory to fabricate HTTP clients that are already aware of the token management plumbing. For this you would register a named client in your startup e.g. like this:

// registers HTTP client that uses the managed user access token
services.AddUserAccessTokenClient("apiClient", configureClient: client =>
{
    client.BaseAddress = new Uri("https://remoteServer/");
});

And then retrieve a client instance like this:

[Route("myApi")]
public class MyApiController : ControllerBase
{
    private readonly IHttpClientFactory _httpClientFactory;

    public MyController(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }
    
    public async Task<IActionResult> Get(string id)
    {
        // create HTTP client with automatic token management
        var client = _httpClientFactory.CreateClient("apiClient");
        
        // call remote API
        var response = await client.GetAsync("remoteApi");

        // rest omitted
    }
}

If you prefer to use typed clients, you can do that as well:

// registers a typed HTTP client with token management support
services.AddHttpClient<MyTypedApiClient>(client =>
{
    client.BaseAddress = new Uri("https://remoteServer/");
})
    .AddUserAccessTokenHandler();

And then use that client, for example like this on a controller’s action method:

public async Task<IActionResult> CallApiAsUserTyped(
    [FromServices] MyTypedClient client)
{
    var response = await client.GetData();
    
    // rest omitted
}

The client will internally always try to use a current and valid access token. If for any reason this is not possible, the 401 status code will be returned to the caller.

Manually revoking refresh tokens

Duende.BFF revokes refresh tokens automatically at logout time (this behavior can be controlled via the options).

If you want to manually revoke the current refresh token, you can use the following code:

    await HttpContext.RevokeUserRefreshTokenAsync();

This will invalidate the refresh token at the token service.