I am trying to get my logged in user and connection id to map user accordingly.
Below is my program.cs file:
var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddSignalR();// Add services to the container.builder.Services.AddRazorComponents().AddInteractiveServerComponents();builder.Services.AddCascadingAuthenticationState();builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();builder.Services.AddScoped<IdentityUserAccessor>();builder.Services.AddScoped<IdentityRedirectManager>();builder.Services.TryAddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();builder.Services.AddAuthorizationCore();builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();builder.Services.AddAuthentication(options =>{ options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; options.RequireAuthenticatedSignIn = true;}).AddIdentityCookies();//Database ConnectionDatabaseConnection connectionString = new(builder.Configuration.GetConnectionString("OffDatabase") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."));builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(DatabaseConnection.ConnectionString), ServiceLifetime.Transient);builder.Services.AddDatabaseDeveloperPageExceptionFilter();builder.Services.AddHttpContextAccessor();builder.Services.AddIdentityCore<ApplicationUser>(options =>{ options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequiredLength = 5; options.Password.RequireNonAlphanumeric = false; options.SignIn.RequireConfirmedEmail = true; options.SignIn.RequireConfirmedAccount = true;}).AddEntityFrameworkStores<ApplicationDbContext>() .AddSignInManager() .AddDefaultTokenProviders() .AddSignInManager<SignInManager<ApplicationUser>>();builder.Services.AddCors();builder.Services.ConfigureApplicationCookie(options =>{ options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Logout"; options.AccessDeniedPath = "/Account/AccessDenied"; options.ExpireTimeSpan = TimeSpan.FromDays(7); options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter; options.SlidingExpiration = true;});builder.Services.AddControllersWithViews();builder.Services.AddRazorPages();builder.WebHost.UseStaticWebAssets();var app = builder.Build();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){ app.UseMigrationsEndPoint();}else{ app.UseExceptionHandler("/Error", createScopeForErrors: true); app.UseHsts();}app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();app.UseMiddleware<BlazorCookieLoginMiddleware>();app.UseAuthentication();app.UseAuthorization();app.UseAntiforgery();app.MapRazorComponents<App>() .AddAdditionalAssemblies(typeof(AuthenticationLibrary._Imports).Assembly) .AddInteractiveServerRenderMode();app.MapAdditionalIdentityEndpoints();app.MapControllers();app.MapHub<ItemAccessHub>("/itemaccesshub");app.Run();Below is my ItemAccessHub:
using Microsoft.AspNetCore.Identity;using Microsoft.AspNetCore.SignalR;using System.Collections.Concurrent;using System.Security.Claims;using System.Web.Mvc;namespace Admin.Hubs{ [Authorize] public class ItemAccessHub : Hub { private static readonly ConcurrentDictionary<string, (string UserEmail, string Module, string UserName)> RowLocks = new(); private static readonly ConcurrentDictionary<string, string> UserConnections = new(); public override Task OnConnectedAsync() { var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value; var userName = Context.User?.Identity?.Name; // Assuming the username is stored in the Name claim if (!string.IsNullOrEmpty(userEmail)) { UserConnections[userEmail] = Context.ConnectionId; } return base.OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value; if (!string.IsNullOrEmpty(userEmail) && UserConnections.ContainsKey(userEmail)) { // Remove the user's connection ID UserConnections.TryRemove(userEmail, out _); var lockedRows = RowLocks.Where(x => x.Value.UserEmail == userEmail).Select(x => x.Key).ToList(); foreach (var rowId in lockedRows) { RowLocks.TryRemove(rowId, out _); Clients.Others.SendAsync("RowUnlocked", rowId); } } return base.OnDisconnectedAsync(exception); } public Task LockRow(string rowId, string module) { var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value; var userName = Context.User?.Identity?.Name; if (!string.IsNullOrEmpty(userEmail) && !RowLocks.ContainsKey(rowId)) { RowLocks[rowId] = (userEmail, module, userName); return Clients.Others.SendAsync("RowLocked", rowId, userName); } return Task.CompletedTask; } public Task UnlockRow(string rowId) { var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value; if (!string.IsNullOrEmpty(userEmail) && RowLocks.ContainsKey(rowId)) { if (RowLocks.TryGetValue(rowId, out var lockInfo) && lockInfo.UserEmail == userEmail) { RowLocks.TryRemove(rowId, out _); return Clients.Others.SendAsync("RowUnlocked", rowId); } } return Task.CompletedTask; } }}Below is my Middleware code:
public class BlazorCookieLoginMiddleware{ public static IDictionary<Guid, LoginInfo> Logins { get; private set; } = new ConcurrentDictionary<Guid, LoginInfo>(); private readonly RequestDelegate _next; public BlazorCookieLoginMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context, SignInManager<ApplicationUser> signInMgr) { // Skip Blazor-related paths if (context.Request.Path.Value.StartsWith("/_blazor") || context.Request.Path.Value.StartsWith("/_framework")) { await _next(context); return; } if (context.Request.Path == "/login" && context.Request.Query.ContainsKey("key")) { var key = Guid.Parse(context.Request.Query["key"]); var info = Logins[key]; var result = await signInMgr.PasswordSignInAsync(info.Email, info.Password, false, lockoutOnFailure: true); info.Password = null; if (result.Succeeded) { Logins.Remove(key); context.Response.Redirect("/"); return; } else if (result.RequiresTwoFactor) { //TODO: redirect to 2FA razor component context.Response.Redirect("/loginwith2fa/" + key); return; } else { //TODO: Proper error handling context.Response.Redirect("/loginfailed"); return; } } else if (context.Request.Path == "/logout" && context.Request.Query.ContainsKey("key")) { var key = Guid.Parse(context.Request.Query["key"]); await signInMgr.SignOutAsync(); Logins.Remove(key); context.Response.Redirect("/"); return; } else { await _next.Invoke(context); return; } }}This is how create connection and call:
hubConnection = new HubConnectionBuilder() .WithUrl(UriHelper.ToAbsoluteUri("/itemaccesshub")) .Build();hubConnection.On<string, string>("RowLocked", (rowId, username) =>{ rowLocks[rowId] = username; InvokeAsync(StateHasChanged);});hubConnection.On<string>("RowUnlocked", rowId =>{ rowLocks.Remove(rowId); InvokeAsync(StateHasChanged);});await hubConnection.StartAsync();However, my context is always null:
I have gone through all documentation on Microsoft but not able to make it work. I need context information (Authorized user) details for keeping track and manage connection id.

