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

Blazor Custom AuthenticationStateProvider Not Setting Cookies or Authorizing Pages

$
0
0

In my Blazor project (.Client and .Server), I implemented a CustomAuthenticationStateProvider and used [Authorize] on a protected page, but authentication fails—no cookie is set, and the page acts as if the user is unauthenticated.

Key Setup:

AuthController.cs:

using Dragon.Authentications;using Dragon.Models;using Microsoft.AspNetCore.Authentication.Cookies;using Microsoft.AspNetCore.Authentication;using Microsoft.AspNetCore.Identity;using Microsoft.AspNetCore.Mvc;using Microsoft.IdentityModel.Tokens;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;using System.Text;using Microsoft.AspNetCore.Authorization;namespace Dragon.Controllers{    [Route("api/auth")]    [ApiController]    public class AuthController : ControllerBase    {        private readonly DatabaseContext _dbContext;        public AuthController(DatabaseContext dbContext)        {            _dbContext = dbContext;        }        [HttpPost("login")]        [Authorize]        [AllowAnonymous]        public async Task<IActionResult> Login([FromBody] LoginModel request)        {            var user = _dbContext.Users.FirstOrDefault(u => u.Username == request.Username && u.Active);            if (user == null || user.Password != request.Password)             {                return Unauthorized(new { Message = "Invalid credentials." });            }            var identity = new ClaimsIdentity(new[]            {                new Claim(ClaimTypes.Name, user.FirstName),                new Claim(ClaimTypes.Sid, user.Id.ToString()),                new Claim(ClaimTypes.Expiration, DateTimeOffset.UtcNow.AddMinutes(300).ToString())            }, "Cookies");            var principal = new ClaimsPrincipal(identity);            await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal,                new AuthenticationProperties                {                    IsPersistent = true,                    AllowRefresh = true,                    ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(30),                    IssuedUtc = DateTime.UtcNow,                });            HttpContext.User = principal;            return Ok();        }    }}

Routes.razor:

<CascadingAuthenticationState><Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Client._Imports).Assembly }"><Found Context="routeData"><RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" /><FocusOnNavigate RouteData="@routeData" Selector="h1" /></Found></Router></CascadingAuthenticationState>

CustomAuthenticationStateProvider.cs:

namespace Dragon.Authentications{    public class CustomAuthenticationStateProvider : AuthenticationStateProvider    {        private readonly IHttpContextAccessor _httpContextAccessor;        public CustomAuthenticationStateProvider(IHttpContextAccessor httpContextAccessor)        {            _httpContextAccessor = httpContextAccessor;        }        public override Task<AuthenticationState> GetAuthenticationStateAsync()        {            var httpContext = _httpContextAccessor.HttpContext;            var user = httpContext?.User;            if (user?.Identity != null && user.Identity.IsAuthenticated)            {                return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(user)));            }            // Return unauthenticated state            return Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())));        }    }}

Program.cs (.Server):

var builder = WebApplication.CreateBuilder(args);// Register database contextbuilder.Services.AddDbContext<DatabaseContext>(options =>    options.UseSqlServer("Server=localhost;Database=offline;User Id=sa;Password=admin;TrustServerCertificate=True"));builder.Services.AddRazorComponents()    .AddInteractiveServerComponents()    .AddInteractiveWebAssemblyComponents();builder.Services.AddSingleton<ISessionSettings, SettingService>();builder.Services.AddSingleton<IOptionService, OptionService>();builder.Services.AddSingleton<IHtmlProcessorService, HtmlProcessorService>();builder.Services.AddScoped<IEncodingService, EncodingService>();builder.Services.AddHttpClient();builder.Services.AddSingleton<ServerCache>();builder.Services.AddAntiforgery();builder.Services.AddControllers(o =>{    o.AllowEmptyInputInBodyModelBinding = true;    o.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;});builder.Services.AddServerSideBlazor(options =>{    options.DetailedErrors = true;});builder.Services.AddScoped<ModalService>();builder.Services.AddScoped<ToastService>();builder.Services.AddScoped<EmailService>();builder.Services.AddScoped<SupportChatService>();builder.Services.AddScoped(sp => new HttpClient{    BaseAddress = new Uri(builder.Configuration["BaseUrl:ApiUrl"])});builder.Services.AddSignalR(op => { op.MaximumReceiveMessageSize = 32 * 1024 * 1024; }).AddMessagePackProtocol();builder.Services.AddResponseCompression(options =>{    options.EnableForHttps = true;});builder.Services.AddCascadingAuthenticationState();builder.Services.AddHttpContextAccessor();builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();builder.Services.AddAuthentication(o =>{    o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;    o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;    o.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>{    // Cookie settings    o.Cookie.HttpOnly = true;                          // Prevent client-side access    o.Cookie.SecurePolicy = CookieSecurePolicy.None;   // Use `Always` for HTTPS in production; `None` for local testing    o.Cookie.Name = "DragonAuth";                      // Custom cookie name    o.Cookie.SameSite = SameSiteMode.Lax;              // Allows cookie to be sent on same-site navigation    o.ExpireTimeSpan = TimeSpan.FromMinutes(30);       // Cookie expiration (adjust as needed)    o.SlidingExpiration = true;                        // Refresh expiration on activity    // Login and redirect paths    o.LoginPath = "/login/";                           // Path to login page    o.AccessDeniedPath = "/access-denied";             // Path to handle unauthorized access    o.LogoutPath = "/logout/";                         // Path to logout endpoint    // Cookie validation and events    o.Events = new CookieAuthenticationEvents    {        OnValidatePrincipal = ctx =>        {            // Custom validation logic            if (ctx.Principal?.Identity?.IsAuthenticated ?? false)            {                var expirationClaim = ctx.Principal.FindFirst(ClaimTypes.Expiration)?.Value;                if (DateTimeOffset.TryParse(expirationClaim, out var expiration) && expiration < DateTimeOffset.UtcNow)                {                    ctx.RejectPrincipal();  // Reject expired tokens                    return ctx.HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);                }            }            else            {                ctx.RejectPrincipal();  // No identity or not authenticated            }            return Task.CompletedTask;        }    };});builder.Services.AddAuthorizationCore();builder.Services.AddAuthorization();var app = builder.Build();if (app.Environment.IsDevelopment()){    app.UseWebAssemblyDebugging();}else{    app.UseExceptionHandler("/Error", createScopeForErrors: true);    app.UseHsts();}app.UseResponseCompression();app.UseHttpsRedirection();app.UseStaticFiles();app.UseRouting();app.UseAntiforgery();var cookiePolicyOptions = new CookiePolicyOptions{    MinimumSameSitePolicy = SameSiteMode.Strict,};app.UseCookiePolicy(cookiePolicyOptions);app.UseAuthentication();app.UseAuthorization();// Blazor app routingapp.MapRazorComponents<App>()    .AddInteractiveServerRenderMode()    .AddInteractiveWebAssemblyRenderMode();app.MapControllers();app.MapHub<TeleHub>("/teleHub");app.MapHub<Dragon.Hubs.SupportChat>("/supportChat");app.Run();

Program.cs (.Client):

var builder = WebAssemblyHostBuilder.CreateDefault(args);builder.Logging.SetMinimumLevel(LogLevel.Debug);builder.Services.AddSingleton<IUserSettingsService, UserSettingsService>();builder.Services.AddScoped<ToastService>();builder.Services.AddScoped<ModalService>();await builder.Build().RunAsync();

Login.razor (on Server project):

@page "/login"@inject NavigationManager Navigation@inject HttpClient Http@inject AuthenticationStateProvider AuthenticationStateProvider@rendermode @(new InteractiveAutoRenderMode(false))<h3>Login</h3>@if (loginFailed){<p class="text-danger">Invalid username or password. Please try again.</p>}<EditForm Model="loginModel" FormName="loginForm" OnValidSubmit="HandleLogin"><DataAnnotationsValidator /><ValidationSummary /><div><label for="username">Username:</label><InputText id="username" @bind-Value="loginModel.Username" /></div><div><label for="password">Password:</label><InputText id="password" @bind-Value="loginModel.Password" InputType="password" /></div><button type="submit">Login</button></EditForm>@code {    private LoginModel loginModel = new LoginModel();    private bool loginFailed = false;    private async Task HandleLogin()    {        loginFailed = false;        try        {            var response = await Http.PostAsJsonAsync("/api/auth/login", loginModel);            if (response.IsSuccessStatusCode)            {                //await AuthenticationStateProvider.MarkUserAsAuthenticated(token);                //await ((CustomAuthenticationStateProvider)AuthenticationStateProvider).GetAuthenticationStateAsync();                Navigation.NavigateTo("/");  // Successfully logged in, navigate to t            }            else            {                // Login failed                loginFailed = true;            }        }        catch        {            loginFailed = true;        }    }}

Problem:Despite these setups, cookies aren't created in the browser, and protected pages are inaccessible. What could be the missing step to make authentication work correctly in this setup?


Viewing all articles
Browse latest Browse all 4839

Trending Articles



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