Search code examples
c#asp.net-coreblazor-server-side.net-8.0

Blazor Server .Net 8 Cookie Authentication without Identity


I was trying to do a custom authentication using Blazor Server in .Net 8 but until now not succeed. I'm new to this Blazor framework and please check my work flow why I keep getting the error. I've read some of the article but things in Blazor are moving so fast and I've tried so many method but still unsuccessful.

My page tree:

My page tree

Program.cs

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(x =>
    {
        x.LoginPath = "/login";
    });
builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();

///////////////////////////

app.UseAuthentication();
app.UseAuthorization();

Login.razor

@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Authentication.Cookies
@using System.Security.Claims

@* some razor component *@

@code{
    [CascadingParameter]
    public HttpContext httpcontext { get; set; } = default!;

    public async Task ClickLogin()
    {
        _loginbtnLoading = true;
        if (!string.IsNullOrEmpty(_ntid) && !string.IsNullOrEmpty(_password))
        {
            await Task.Delay(2000);
            //I'm using LDAP for checking user signin
            var loginOK = ILoginServices.CheckLoginStatus(_ntid, _password);
            if (loginOK.success)
            {

                var claims = new List<Claim>();
                claims.Add(new Claim(ClaimTypes.Name, _ntid)); // add more claims

                var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);

                var principal = new ClaimsPrincipal(claimsIdentity);

                // Sign in the user
                await httpcontext.SignInAsync(principal);

                NavManager.NavigateTo("/home");
            }
            else
            {
                Snackbar.Add(loginOK.errorMessage, Severity.Error);
            }
        }
        else
        {
            Snackbar.Add("Please key in all the required info", Severity.Error);
        }
        _loginbtnLoading = false;
    }

}

The error produced was System.InvalidOperationException: Headers are read-only, response has already started.


Solution

  • According to your codes, I created a test demo on my side ,it works well. Since you don't post the related codes to show what's your make up for this blazor. I will post what I am doing on my side.

    Please notice: You should make sure the login.razor is static SSR.

    More details, you could refer to below codes:

    @page "/login"
    @using Microsoft.AspNetCore.Authentication
    @using Microsoft.AspNetCore.Authentication.Cookies
    @using System.Security.Claims
    @inject NavigationManager NavManager
    
    <h3>Login</h3>
    
    <EditForm method="post" FormName="ClickLogin" OnSubmit="ClickLogin" Model="UserCredentials">
     
        <InputText @bind-Value="UserCredentials.Username" placeholder="Username"></InputText>
        <InputText @bind-Value="UserCredentials.Password" placeholder="Password" />
    
        <button type="submit" > SignIn</button>
    
    </EditForm>
    
    
    @code {
        [CascadingParameter]
        public HttpContext httpcontext { get; set; } = default!;
    
        [SupplyParameterFromForm]
        public Credential UserCredentials { get; set; } = new Credential();
    
        public async Task ClickLogin()
        {
            var claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.Name, "test")); // add more claims
    
            var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
    
            var principal = new ClaimsPrincipal(claimsIdentity);
    
            // Sign in the user
            await httpcontext.SignInAsync(principal);
    
            NavManager.NavigateTo("/");
    
        }
    }
    

    Prgram.cs:

    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddRazorComponents()
        .AddInteractiveServerComponents();
    builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie(x =>
        {
            x.LoginPath = "/login";
        });
    builder.Services.AddAuthorization();
    builder.Services.AddCascadingAuthenticationState();
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (!app.Environment.IsDevelopment())
    {
        app.UseExceptionHandler("/Error", createScopeForErrors: true);
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    
    app.UseHttpsRedirection();
    
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseAntiforgery();
    app.UseStaticFiles();
    
    
    app.MapRazorComponents<App>()
        .AddInteractiveServerRenderMode();
    
    app.Run();