How to implement JWT autentication in ASP Core Net 6 and gRPC?

ComptonAlvaro 166 Reputation points
2022-01-08T18:27:18.013+00:00

I am trying to learn how to use JWT authentication in ASP Core Net 6 and gRPC. I am following this example.

The problem is that they are using the StartUp.cs file, that doesn't exist in the ASP Core Net 6. So I am trying some options.

I have done some tries, but I get the error about mismatch certificate.

So I would like to know if there any documentation about how to use this kind of authentication in Net 6 or if someone could help me how to do it.

This is my code in the service:

program.cs

using GestorOrdenadores.Service.Grpc.DotNet.Services;
using ProtoBuf.Grpc.Server;
using Microsoft.AspNetCore.Server.Kestrel.Core;

using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.JwtBearer;



var builder = WebApplication.CreateBuilder(args);

//Se configura kestrel, donde se puede indicar los protocolos, certificados, puertos de escucha... etc.
builder.WebHost.ConfigureKestrel((context, options) =>
{
    options.ListenAnyIP(5004, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        listenOptions.UseHttps();
    });
});



builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.TokenValidationParameters =
                        new TokenValidationParameters
                        {
                            ValidateAudience = false,
                            ValidateIssuer = false,
                            ValidateActor = false,
                            ValidateLifetime = true,
                            IssuerSigningKey = GestorOrdenadores.Service.Grpc.DotNet.Autenticacion.SecurityKey
                        };
                });

// 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.AddCodeFirstGrpc();

var app = builder.Build();

// Configure the HTTP request pipeline.
app.MapGrpcService<GreeterService>();


//Configuración para utilizar JWT (un token), para la autenticación de la aplicación.
app.MapGet("/", context =>
{
    GestorOrdenadores.Service.Grpc.DotNet.Autenticacion miAutenticador = new GestorOrdenadores.Service.Grpc.DotNet.Autenticacion();

    return context.Response.WriteAsync(miAutenticador.GenerateJwtToken(context.Request.Query["name"]));
});


app.Run();

Authentication auxiliar class:

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Tokens;


namespace GestorOrdenadores.Service.Grpc.DotNet
{
    internal class Autenticacion
    {
        private readonly JwtSecurityTokenHandler JwtTokenHandler;
        internal static readonly SymmetricSecurityKey SecurityKey = new SymmetricSecurityKey(Guid.NewGuid().ToByteArray());


        public Autenticacion()
        {
            JwtTokenHandler = new JwtSecurityTokenHandler();
        }


        internal string GenerateJwtToken(string paramName)
        {
            if (string.IsNullOrEmpty(paramName))
            {
                throw new InvalidOperationException("Name is not specified.");
            }

            var claims = new[] { new Claim(ClaimTypes.Name, paramName) };
            var credentials = new SigningCredentials(SecurityKey, SecurityAlgorithms.HmacSha256);
            var token = new JwtSecurityToken("ExampleServer", "ExampleClients", claims, expires: DateTime.Now.AddSeconds(60), signingCredentials: credentials);
            return JwtTokenHandler.WriteToken(token);
        }
    }
}

My client:

public class ClienteGrpcDotNet
{
    #region constructores
    public ClienteGrpcDotNet()
    {
        var channel = GrpcChannel.ForAddress("https://192.168.1.137:5001");
        _client = channel.CreateGrpcService<IGreeterService>();


        Authenticate("https://192.168.1.2:5001");
    }
    #endregion constructores



    private IGreeterService _client;



    public async Task<string?> SayHelloAsync()
    {
        HelloReply miRespuesta = await _client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });

        return miRespuesta.Message;
    }



    #region autenticación
    private static async Task<string> Authenticate(string paramAddress)
    {
        using var httpClient = new HttpClient();
        using var request = new HttpRequestMessage
        {
            RequestUri = new Uri($"{paramAddress}/generateJwtToken?name={HttpUtility.UrlEncode(Environment.UserName)}"),
            Method = HttpMethod.Get,
            Version = new Version(2, 0)
        };
        using var tokenResponse = await httpClient.SendAsync(request);
        tokenResponse.EnsureSuccessStatusCode();

        var token = await tokenResponse.Content.ReadAsStringAsync();

        return token;
    }
    #endregion autenticación
}

Thanks so much.

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,192 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Bruce (SqlWork.com) 56,771 Reputation points
    2022-01-08T20:31:14.003+00:00

    The sample is .net 6. Why don’t you get it working before trying to change to the program to the optional min api

    1 person found this answer helpful.

  2. Bruce (SqlWork.com) 56,771 Reputation points
    2022-01-10T17:26:21.087+00:00

    Its pretty simple to port startup to min code. startup had two callbacks

    the first is ConfigureServices. this is used to add DI services. you put this code inline after:

      var builder = WebApplication.CreateBuilder(args);
    
      // add configure services code here 
      builder.Services.Add....
    

    the second was Configure. This configure the app after being build

     var app = builder.Build();
    
    // put configure code here
    
     app.run();
    

    note: you don't need DI for configure because all the code in inline.

    0 comments No comments