Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4839

Signal R authentication is showing Context details to be null

$
0
0

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:

OnDisconnected

OnConnection

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.


Viewing all articles
Browse latest Browse all 4839

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>