grpc-dotnet icon indicating copy to clipboard operation
grpc-dotnet copied to clipboard

Way to add call credentials to insecure grpc channel without using DI factory

Open Blackclaws opened this issue 3 years ago • 5 comments

There is a documented way to create a gRPC client using the dependency injection framework that allows the usage of call credentials on insecure channels:

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("http://localhost:5060");
    o.ChannelOptionsActions.Add((opt) =>
    {
        opt.UnsafeUseInsecureChannelCallCredentials = true;
    });
}).AddCallCredentials((context, metadata) =>
{
    metadata.Add("Authorization",
        "Bearer ...");
    return Task.CompletedTask;
});

I have not found a way to do the same if I manually create a channel. It seems the classes I would need to use are all internal/private classes. Is this by design or is there simply a way I'm missing to do this?

Blackclaws avatar Jun 30 '22 16:06 Blackclaws

Ok sometimes the solution you tried to find for hours comes right after posting a question. So here it is:

You need to create a new ChannelCredential implementation it seems. This can do the same thing as the CompositeChannelCredentials but doesn't check whether IsComposable is true.

I'm leaving this up though to propose an API change because this seems cumbersome in the extreme.

How about changing the call to ChannelCredentials.Create to also take a boolean value: AcceptUnsafeComposition

Pass that to the constructor of CompositeChannelCredentials and turn the check:

 if (!channelCredentials.IsComposable)

into

 if (AcceptUnsafeComposition || !channelCredentials.IsComposable)

Blackclaws avatar Jun 30 '22 16:06 Blackclaws

Does this not work?

private static GrpcChannel CreateAuthenticatedChannel(string address)
{
    var credentials = CallCredentials.FromInterceptor((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(ChannelCredentials.Insecure, credentials)
        UnsafeUseInsecureChannelCallCredentials = true
    });
    return channel;
}

JamesNK avatar Jul 01 '22 07:07 JamesNK

No, ChannelCredentials.Create fails because ChannelCredentials.Insecure is not composable. You get this error:

Unhandled exception. System.ArgumentException: CallCredentials can't be composed with InsecureCredentials. CallCredentials must be used with secure channel credentials like SslCredentials.

Blackclaws avatar Jul 01 '22 07:07 Blackclaws

Got it. I just discovered the same thing in a test.

Using insecure + channel credentials (i.e. not passed as an argument to a CallOptions.Credentials) wasn't considered. I'll see whether there is a way to fix this.

JamesNK avatar Jul 01 '22 07:07 JamesNK

Fix here: https://github.com/grpc/grpc-dotnet/pull/1802

There is a behavior change here that needs to be discussed before merging.

JamesNK avatar Jul 01 '22 08:07 JamesNK