Why security matters for modern gRPC APIs
Every day, enterprises move more business logic behind high‑performance gRPC endpoints, yet the rise in data breaches shows that encryption alone is no longer sufficient. A recent 2023 report from Gartner indicated that 62% of API‑related incidents involved weak authentication or missing authorization checks. When a .NET 8 service is exposed to multiple micro‑services or external partners, you need a defense‑in‑depth approach: mutual TLS for channel security, Azure Key Vault for secret management, and policy‑based authorization for fine‑grained access control.
Mutual TLS in .NET 8: the basics
Mutual TLS (mTLS) extends the standard TLS handshake by requiring both client and server to present a valid X.509 certificate. In .NET 8 the Kestrel web server can be configured to request client certificates automatically. The following snippet shows how to enable mTLS on the server side while delegating certificate validation to a custom handler.
<?csharp
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
options.ConfigureHttpsDefaults(https =>
{
https.ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode.RequireCertificate;
https.CheckCertificateRevocation = true;
});
});
builder.Services.AddAuthentication()
.AddCertificate(certOptions =>
{
certOptions.AllowedCertificateTypes = CertificateTypes.All;
certOptions.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
// Placeholder for Azure Key Vault validation
return Task.CompletedTask;
}
};
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGrpcService<GreeterService>();
app.Run();
?>Key points:
- ClientCertificateMode.RequireCertificate forces the client to present a cert.
- CheckCertificateRevocation prevents use of revoked credentials.
- The OnCertificateValidated event is where you cross‑check the thumbprint against Azure Key Vault.
Storing and retrieving certificates with Azure Key Vault
Hard‑coding PEM files is a security liability. Azure Key Vault provides a managed repository for certificates, secrets, and keys with role‑based access control (RBAC). To pull a certificate at runtime, add the Azure.Identity package and request the secret by its identifier.
<?csharp
using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
var kvUri = "https://myvault.vault.azure.net/";
var client = new CertificateClient(new Uri(kvUri), new DefaultAzureCredential());
// Retrieve the latest version of the server certificate
KeyVaultCertificateWithPolicy cert = await client.GetCertificateAsync("grpc-server-cert");
X509Certificate2 serverCert = new X509Certificate2(cert.Cer);
// Apply to Kestrel (example inside ConfigureKestrel)
options.ConfigureHttpsDefaults(https =>
{
https.ServerCertificate = serverCert;
https.ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode.RequireCertificate;
});
?>Because DefaultAzureCredential automatically picks up Managed Identity when the app runs in Azure App Service or AKS, you avoid storing client secrets in code.
Policy‑based authorization for gRPC methods
Once the TLS channel is trusted, you still need to decide *who* can call each RPC. .NET 8’s AuthorizationPolicyBuilder lets you define requirements based on claims, roles, or custom assertions. The example below creates a policy that only permits callers with a "role" claim equal to "admin" and applies it to a specific gRPC service.
<?csharp
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
policy.RequireClaim("role", "admin"));
});
app.MapGrpcService<GreeterService>()
.RequireAuthorization("RequireAdmin");
?>Inside the service implementation you can also read the client certificate thumbprint from the HttpContext and map it to a claim, enabling certificate‑bound authorization without extra tokens.
Putting everything together: a minimal secure gRPC service
The following complete Program.cs demonstrates the flow from certificate retrieval to policy enforcement. Replace myvault and grpc-server-cert with your own values.
<?csharp
var builder = WebApplication.CreateBuilder(args);
// 1. Load server cert from Key Vault
var kvUri = "https://myvault.vault.azure.net/";
var certClient = new CertificateClient(new Uri(kvUri), new DefaultAzureCredential());
var cert = await certClient.GetCertificateAsync("grpc-server-cert");
var serverCert = new X509Certificate2(cert.Value.Cer);
// 2. Configure Kestrel for mTLS
builder.WebHost.ConfigureKestrel(opts =>
{
opts.ConfigureHttpsDefaults(https =>
{
https.ServerCertificate = serverCert;
https.ClientCertificateMode = Microsoft.AspNetCore.Server.Kestrel.Https.ClientCertificateMode.RequireCertificate;
https.CheckCertificateRevocation = true;
});
});
// 3. Authentication via client cert
builder.Services.AddAuthentication()
.AddCertificate(certOpts =>
{
certOpts.AllowedCertificateTypes = CertificateTypes.All;
certOpts.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = ctx =>
{
// Example: map thumbprint to role claim
var thumbprint = ctx.ClientCertificate?.Thumbprint;
if (thumbprint == "ABC123DEF456...")
{
var claims = new[] { new Claim("role", "admin") };
ctx.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, ctx.Scheme.Name));
}
return Task.CompletedTask;
}
};
});
// 4. Policy‑based authorization
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("RequireAdmin", p => p.RequireClaim("role", "admin"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGrpcService<GreeterService>().RequireAuthorization("RequireAdmin");
app.Run();
?>Deploy this to Azure Kubernetes Service (AKS) with a Managed Identity that has "Key Vault Secrets User" role on the vault. The service will accept only clients that present a trusted certificate and that are mapped to the "admin" role, guaranteeing confidentiality, integrity, and proper access control.
Testing and monitoring the secure endpoint
After deployment, verify the handshake with grpcurl -cacert client.crt -cert client.crt -key client.key https://myservice:5001 Greeter/SayHello. A successful call returns the expected payload; a failure will show TLS or authorization errors. Integrate Azure Monitor logs to capture authentication events, and set alerts for repeated certificate validation failures, which often indicate a misconfiguration or an attempted breach.
Sources
Microsoft Docs – gRPC for .NET; Azure Key Vault documentation; OWASP API Security Top 10 (2023).
Author: Mahmut Sarıkaya — sarikayadev.com