I have a project that involves using SignalR from a JavaScript application. Since the client is coming from a browser, my application employs JWT authentication for the SignalR client.
In my WebAPI, I have JWT Bearer authentication configured. However, when attempting to use SignalR hub with authentication, I encountered errors, and essentially, I wasn't sure how to call the secure SignalR endpoint. After extensive research on the internet, I have found a solution that I'm going to share in this post so let's discuess about How to call signalr secure endpoint using jwt token core c# server.
In this post we are going to discuess the below point
[Authorize]
    public class ChatHub : Hub
    {
        private readonly IServiceScopeFactory _scopeFactory;
        public ChatHub(IServiceScopeFactory scopeFactory)
        {
            _scopeFactory = scopeFactory;
        }
        
        public override Task OnDisconnectedAsync(Exception exception)
        {
            using (var scope = _scopeFactory.CreateScope())
            {
                var _dbContext = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
             
                var userId =  new Guid(Context.User.GetLoggedInUserClaim(ClaimType.UserId));
                var user = _dbContext.Users.FirstOrDefault(a => a.Id == userId);
                user.IsOnline = false;
                user.LastDisconnectedAt = DateTime.UtcNow;
                _dbContext.SaveChanges();
                Debug.WriteLine("Client disconnected: " + Context.ConnectionId);
                return base.OnDisconnectedAsync(exception);
            }
           
        }
        public override Task OnConnectedAsync()
        {
            using (var scope = _scopeFactory.CreateScope())
            {
                Debug.WriteLine("Client connected: " + Context.ConnectionId);
                var _dbContext = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
                
                var userId = new Guid(Context.User.GetLoggedInUserClaim(ClaimType.UserId));
                var user = _dbContext.Users.FirstOrDefault(a => a.Id == userId);
                user.ConnectionId = Context.ConnectionId;
                user.IsOnline = true;
                user.LastConnectedAt = DateTime.UtcNow;
                _dbContext.SaveChanges();
                return base.OnConnectedAsync();
            }
        }
        public async Task SendNotification(string userId)
        {
            if (Clients != null)
            {
                await Clients.Group(userId.ToLower()).SendAsync("Notification", "New notification recived!");
            }
        }        
       
    }Here is my Program.cs file
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddDbContext<DatabaseContext>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "PetsDevoteeBack", Version = "v1" });
    var securitySchema = new OpenApiSecurityScheme
    {
        Description = @"JWT Authorization header using the Bearer scheme. <br>
                                    Enter 'Bearer' [space] and then your token in the text input below. <br>
                                    Example: 'Bearer 12345abcdef'",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "bearer",
        Reference = new OpenApiReference
        {
            Type = ReferenceType.SecurityScheme,
            Id = "Bearer"
        }
    };
    c.AddSecurityDefinition("Bearer", securitySchema);
    var securityRequirement = new OpenApiSecurityRequirement
                {
                    { securitySchema, new[] { "Bearer" } }
                };
    c.AddSecurityRequirement(securityRequirement);
});
builder.Services.AddSignalR();
builder.Services.AddSingleton<ChatHub>();
builder.Services.AddSingleton<JWTManagerRepository>();
builder.Services.AddSingleton<DbThreadLogic>();
// JWT
builder.Services.AddAuthentication(x =>
{
    x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(o =>
{
    var Key = Encoding.UTF8.GetBytes(builder.Configuration["JWT:Key"]);
    o.SaveToken = true;
    o.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false,
        ValidateAudience = false,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = builder.Configuration["JWT:Issuer"],
        ValidAudience = builder.Configuration["JWT:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Key)
    };
});
builder.Services.AddAuthorization(x =>
{
    x.AddPolicy(AccessLevels.GlobalKey, policy => policy.RequireClaim("Type", ((int)Permissions.Global).ToString()));
});
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme;
})
  .AddCookie()
  .AddGoogle(options =>
  {
      options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
      options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
  });
builder.Services.AddCors(p => p.AddDefaultPolicy(builder =>
{
    builder.WithOrigins("*").AllowAnyMethod().AllowAnyHeader();
}));
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
else
{
    app.UseSwagger();
    app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors(builder => builder
                .AllowAnyHeader()
                .AllowAnyMethod()
                .SetIsOriginAllowed((host) => true)
                .AllowCredentials()
              );
app.UseAuthentication();
app.UseAuthorization();
app.UseStaticFiles();
app.MapControllers();
app.MapHub<ChatHub>("/Chating");
app.Run();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.0.1/css/toastr.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.0.1/js/toastr.js"></script>
    
    <script type="text/javascript">
        // Authenticate user and obtain JWT token
        const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJUeXBlIjoiMCIsIlVzZXJJZCI6IjYxYTRmZjYwLWY2ZWYtZWUxMS05Y2JiLWE0YmYwMTZjZTU2ZCIsIm5iZiI6MTcxMjA2MTg4NiwiZXhwIjoxNzEyMTQ4Mjg2LCJpYXQiOjE3MTIwNjE4ODZ9.SPYHa4BnQlF-GRAeq7pyOlEZkpuJ3eXwcxIzcJCK2ts";
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("https://localhost:7140/Chating",{
                accessTokenFactory: () => token
            })
            .configureLogging(signalR.LogLevel.Information)
            .build();
        async function start() {
            try {
                await connection.start();              
                console.log("SignalR Connected.");
                
            } catch (err) {
                console.log(err);
                setTimeout(start, 5000);
            }
        };
        connection.onclose(async () => {
            await start();
        });
        // Listen for events
        connection.on("Notification", async (message) => {          
            alert(message)
        });
        
        // Handle token expiration and renew the token if necessary
        // Example logic to renew token
        function renewToken() {
            // Implement token renewal logic in case if token expire
        }
        // Renew token every 60 minutes
        setInterval(renewToken, 60 * 60 * 1000);
        // Start the connection.
        start();       
    </script> Above code exanple establishes a connection to the SignalR hub using the provided JWT token for authentication. It also listens for events emitted by the hub and handles token expiration by renewing the token periodically.