I followed the below solution and moved code into library project. It works fine if I have hub connection in Blazor Server project but doesn't work when moved to library project:
Adding cookies to SignalR Core requests
I have below service in the Library Razor project, that hold implementation of hub connection setup and is shared among two Blazor server apps:
public class UserHubService : IAsyncDisposable{ private HubConnection? _hubConnection; public async Task<bool> InitializeHubAsync(string baseUrl, string hubPath, string subscribeToGroup, Guid? siteId, string userName) { UserName = userName; var uri = new Uri(new Uri(baseUrl), hubPath); //_hubConnection = new HubConnectionBuilder() // .WithUrl(new Uri(new Uri(baseUrl), hubPath)) // .Build(); _hubConnection = new HubConnectionBuilder() .WithUrl(uri, opti => { if (_httpContextAccessor.HttpContext != null) foreach (var c in _httpContextAccessor.HttpContext.Request.Cookies) { opti.Cookies.Add(new Cookie(c.Key, c.Value) { Domain = uri.Host, // Set the domain of the cookie Path = "/" // Set the path of the cookie }); } }) .Build(); _hubConnection.On<Guid?, ProductModel, string>(SignalR_Method.TouchProductReceiveNotification, async (siteID, product, messageType) => { if (_subscribedMessageTypes.Contains(messageType)) { await HandleNotificationAsync(siteID, product, messageType); } }); try { await _hubConnection.StartAsync(); await _hubConnection.InvokeAsync(subscribeToGroup, siteId); return true; } catch (Exception ex) { return false; // Handle exception (e.g., log it) } }}The above service is injected in page and called from Server app:
await ProductNotificationHubService.InitializeHubAsync(baseUrl, hubPath, SignalR_Method.SubscribeToTouchSiteGroup, Site.Site_ID, _userInfo.UserName)Below is the hub again in Library razor project:
[Authorize]public class NotificationHub : Hub{ private static readonly ConcurrentDictionary<string, List<string>> UserConnections = new(); [Authorize] 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.AddOrUpdate( userEmail, new List<string> { Context.ConnectionId }, // Add a new list with the current connection ID (key, existingConnections) => { if (!existingConnections.Contains(Context.ConnectionId)) { existingConnections.Add(Context.ConnectionId); // Add the connection ID to the existing list } return existingConnections; }); } return base.OnConnectedAsync(); } [Authorize] public override Task OnDisconnectedAsync(Exception exception) { var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value; var connectionID = Context.ConnectionId; if (!string.IsNullOrEmpty(userEmail)) { if (UserConnections.TryGetValue(userEmail, out var connections)) { // Remove the specific connection ID connections.Remove(Context.ConnectionId); // If no more connections exist for this user, remove the user entry from the dictionary if (connections.Count == 0) { UserConnections.TryRemove(userEmail, out _); } } } return base.OnDisconnectedAsync(exception); }}I have confirmed that cookies are being set properly:
_hubConnection = new HubConnectionBuilder().WithUrl(uri, opti =>{ if (_httpContextAccessor.HttpContext != null) foreach (var c in _httpContextAccessor.HttpContext.Request.Cookies) { opti.Cookies.Add(new Cookie(c.Key, c.Value) { Domain = uri.Host, // Set the domain of the cookie Path = "/" // Set the path of the cookie }); }}).Build();In the program.cs file:
builder.Services.AddAuthentication(options =>{ options.DefaultScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ExternalScheme; options.RequireAuthenticatedSignIn = true;}).AddCookie(options =>{ options.LoginPath = "/Account/Login/"; options.LogoutPath = "/Account/Logout/"; options.AccessDeniedPath = "/Account/AccessDenied"; options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter; options.Cookie.HttpOnly = true; options.SlidingExpiration = true; options.ExpireTimeSpan = TimeSpan.FromSeconds(30);}).AddIdentityCookies();app.UseAuthentication();app.UseAuthorization();The connection is successful and connected however, the issue I am having is that userEmail in below line is always null:
var userEmail = Context.User?.FindFirst(ClaimTypes.Email)?.Value;