I am trying to Azure AD authentication with gRPC-Web in a blazor webassembly app. I am using protobuf-net to help me with the serialization. I am not sure how to pass the token to have the server side recognize it. this is what I have:
var headers = new Metadata
{
{ "Authorization", $"Bearer {Token}" }
};
and, I am sending that as a parameter in the method I want to consume
var result = await Client.CreateCustomer(this.customer, headers);
This is how the service is injected:
builder.Services.AddTransient(services =>
{
var httpClient = new HttpClient(new GrpcWebHandler(GrpcWebMode.GrpcWeb, new HttpClientHandler()));
var channel = Grpc.Net.Client.GrpcChannel.ForAddress("****", new GrpcChannelOptions { HttpClient = httpClient });
return channel.CreateGrpcService<Application.Services.ICustomerService<ServerCallContext>>();
});
This is how the service is published:
endpoints.MapGrpcService<CustomerService>().RequireAuthorization().EnableGrpcWeb()
and, this is the implementation:
public class CustomerService : ICustomerService<ServerCallContext>
{
[Authorize]
public async ValueTask<Customer> CreateCustomer(Customer customerDTO, ServerCallContext context)
{****}
}
the error I am getting is cannot convert from 'Grpc.Core.Metadata' to 'Grpc.Core.ServerCallContext' which is kind of obvious.
The reference I have found uses Metadata but is ServerCallContext the one I am supposed to use https://learn.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/metadata so what I am missing, what I am doing wrong, how to properly use both using protobuf-net?
It looks like the problem here is that you're using ServerCallContext
in the method signature; the underlying gRPC core has separate client/server context APIs, but this is not amenable to use on an agnostic interface, and as such, protobuf-net.Grpc unifies these two APIs, via CallContext
. So: instead of:
async ValueTask<Customer> CreateCustomer(Customer customerDTO, ServerCallContext context)
for the signature, consider:
async ValueTask<Customer> CreateCustomer(Customer customerDTO, CallContext context)
or
async ValueTask<Customer> CreateCustomer(Customer customerDTO, CallContext context = default)
The CallContext
API exposes the common server-side and client-side APIs (headers, cancellation, etc) in a single way, or you can use (for example) context.ServerCallContext
to get the server-specific API if needed (this will throw an exception if used on a client-context). For client-side usage, a CallContext
can be constructed from a CallOptions
, which is the core gRPC client-side API, for example:
var result = await service.CreateCustomer(customer, new CallOptions(headers));
I'm open to the idea of allowing CallContext
to be created directly from Metadata
/ CancellationToken
etc (allowing var result = await service.CreateCustomer(customer, headers);
) - but it doesn't seem essential.