Featured
gRPC Authenticate and Authorize (Server & Client)
Last modified: May 24, 2022In this article, we learn how to create a gPRC client to authenticate a gRPC Server/Resources
We require
- JWT Token Check out this article to learn how to generate a JWT token
- A gRPC Server
- A gRPC Client
1. gRPC Server
1.1. Create a project
1.2 Install Nuget Package
Microsoft.AspNetCore.Authentication.JwtBearer
1.3 JWT Appsetting.json
"Jwt": {
"Key": "ertwet3245sgf2342werwergww4352345",
"Issuer": "https://localhost:7096/",
"Audience": "https://localhost:7006/"
}
1.4 Configure JWT Token Authenticate and Authorize in Program.cs
using gRPCServer;
using gRPCServer.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Additional configuration is required to successfully run gRPC on macOS.
// For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682
// Add services to the container.
builder.Services.AddGrpc();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new()
{
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Audience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapGrpcService<GreeterService>();
app.MapGrpcService<MessageService>(); //register MessageService
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
app.UseAuthentication(); ;
app.UseAuthorization();
app.Run();
1.5 Create a Proto file and set it server
We create a simple method to print the message to console
syntax = "proto3";
option csharp_namespace = "gRPCServer";
package message;
service MessageProvider{
rpc PrintMessage(Empty) returns (Empty);
}
message Empty {}
1.6 Create a Message Service, the implementation of Proto File
- Create MessageService class file and paste with the following code. Basically, we are protecting the endpoints and check if user is authenticate, if yes print user's name.
using Grpc.Core;
using Microsoft.AspNetCore.Authorization;
namespace gRPCServer
{
[Authorize]
public class MessageService: MessageProvider.MessageProviderBase
{
public override Task<Empty> PrintMessage(Empty request, ServerCallContext context)
{
var user = context.GetHttpContext().User;
Console.WriteLine(user.Identity.Name);
return Task.FromResult<Empty>(new Empty());
}
}
}
Consuming gRPC Server
2. Client (ASP.NET 6 MVC)
- Create an ASP.NET 6 MVC project
- Copy the message.proto from server and paste it to client
- rename csharp_namespace to gRPC_Client
- Right click message.proto as mark it as client
2.1 Install Nuget Packages
2.2 Get JWT Token
- Get JWT token from JWT Auth Server by passing username and password
- Get it in Home Controller, Index Action
- create a UserDto (record UserDto(string UserName, string Password);)
Method 1: Make use of MetaData header to pass thw JWT token
using (var httpClient = new HttpClient())
{
var user = new UserDto(UserName: "[email protected]", Password: "admin");
var json = JsonConvert.SerializeObject(user);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var response = await httpClient.PostAsync("https://localhost:7096/auth/getToken", content))
{
var token = await response.Content.ReadAsStringAsync();
token = token.Substring(1, token.Length - 2);
var headers = new Metadata();
headers.Add("Authorization", $"Bearer {token}");
string GrpcChannelURL = "https://localhost:7006";
using var channel = GrpcChannel.ForAddress(GrpcChannelURL);
var message = new MessageProviderClient(channel);
var products = message.PrintMessage(new Empty { }, headers);
}
}
Test
Method 2: Make use of service (DI)
Install an additional Nuget Package for this method
Create a class and interface to get token from JWT Auth Server
record UserDto(string UserName, string Password);
public interface ITokenProvider
{
Task<string> GetToken();
}
public class TokenProvider : ITokenProvider
{
private string _token;
public async Task<string> GetToken()
{
if (string.IsNullOrEmpty(_token))
{
using (var httpClient = new HttpClient())
{
//in your pass username from User UI to this class
var user = new UserDto(UserName: "[email protected]", Password: "admin");
var json = JsonConvert.SerializeObject(user);
var content = new StringContent(json, Encoding.UTF8, "application/json");
using (var response = await httpClient.PostAsync("https://localhost:7096/auth/getToken", content))
{
var token = await response.Content.ReadAsStringAsync();
_token = token.Substring(1, token.Length - 2);
}
}
}
return _token;
}
}
Register Auth DI and configure gRPC client client factorym with authentication
builder.Services.AddScoped<ITokenProvider, TokenProvider>();
builder.Services.AddGrpcClient<MessageProvider.MessageProviderClient>(o =>
{
o.Address = new Uri("https://localhost:7006");
})
.AddCallCredentials(async (context, metadata, serviceProvider) =>
{
var provider = serviceProvider.GetRequiredService<ITokenProvider>();
var token = await provider.GetToken();
metadata.Add("Authorization", $"Bearer {token}");
});