Efcore: Cosmos: ASP.NET Core with AddDbContext combined with IHttpClientFactory and a delegating handler for managed identity

Created on 29 Oct 2020  路  5Comments  路  Source: dotnet/efcore

When using ASP.NET Core, and specifying DbContext for CosmosDB with services.AddDbContext(...) in startup.cs; is it possible to configure the underlying CosmosClient to use IHttpClientFactory? Further more, is it possible to add a delegating handler to the HttpClient?

In pseudo-code it might look something like the following, albeit the following does not work:

services.AddHttpClient<CosmosClient>().AddHttpMessageHandler<CosmosManagedIdentityDelegatingHandler>();

services.AddSingleton<CosmosClient>(serviceProvider =>
{
    var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();

    var cosmosClientOptions = new CosmosClientOptions()
    {
        HttpClientFactory = httpClientFactory.CreateClient
    };   
    return new CosmosClient(connectionString, cosmosClientOptions);
});

// Here the CosmosClient defined above, using HttpClientFactory and the delegating handler,
// should be used, but in this code it isn't.
// What is missing?
services.AddDbContext<MyDbContext>(options =>
{
    options.UseCosmos(endpoint, dbName);
});

With a delegating handler looking something like this:

public class CosmosManagedIdentityDelegatingHandler : DelegatingHandler
{
    private readonly string[] _cosmosScope = {"https://management.azure.com/"};
    private readonly TokenCredential _credential = new ChainedTokenCredential(
        new ManagedIdentityCredential(),
        new EnvironmentCredential());

    public CosmosManagedIdentityDelegatingHandler() { }

    // Handles the request for authentication token and adds it as bearer token for the outgoing request.
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var tokenRequestContext = new TokenRequestContext(_cosmosScope);

        var token = await _credential.GetTokenAsync(tokenRequestContext, default);

        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);

        return await base.SendAsync(request, cancellationToken);
    }
}

EF Core version:
Database provider: (e.g. Microsoft.EntityFrameworkCore.CosmosDb)
Target framework: .NET Core 3.1/ .NET 5.0)

closed-duplicate customer-reported

Most helpful comment

@1iveowl We're planning now for EF Core 6.0. There's a very good chance that this will make it in.

All 5 comments

Duplicate of #21274

Duplicate of #21274

I wonder if this would as a work-around?

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    Database.GetCosmosClient().ClientOptions.HttpClientFactory = _httpClientFactory.CreateClient;

    base.OnConfiguring(optionsBuilder);
}

Update:
Didn't work, throws:

An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside 'OnConfiguring' since it is still being configured at this point. This can happen if a second operation is started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

@1iveowl Closing this as a duplicate. I discussed with @AndriySvyryd, but unfortunately we don't know of a workaround at this time.

I understand.

I hope at some later point that there will be a solution. Without it I don't currently know how to utilize Azure Managed Identity with Cosmos and EF.

Utilizing HttpClientFactory seem to promis both a solution to Managed Identity as well as providing the benefits of HttpClientFactory.

Would love to work on it,but doesn't currently have the bandwidth.

@1iveowl We're planning now for EF Core 6.0. There's a very good chance that this will make it in.

Was this page helpful?
0 / 5 - 0 ratings