Way to add call credentials to insecure grpc channel without using DI factory
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?
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)
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;
}
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.
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.
Fix here: https://github.com/grpc/grpc-dotnet/pull/1802
There is a behavior change here that needs to be discussed before merging.