question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Blazor] In wasm (client-side blazor) HttpClient post request does not save cookie in browser

See original GitHub issue

Describe the bug

I have a Blazor hosted project set-up with identity authentication. Using 3.0 preview 4, after an HttpClient request to the server Login api, the response correctly contains a Set-Cookie header with the identity cookie. However the cookie is not stored in the browser making subsequent requests unauthorized. This was working in preview 3. I am doing something wrong ?

My Setup

Startup.cs (Server project)

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => false;
    });

    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseLazyLoadingProxies().UseSqlite("Filename=data.db"));
    services.AddCors();

    services.AddIdentity<ApplicationUser, IdentityRole<Guid>>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddDefaultTokenProviders();

    services.ConfigureApplicationCookie(options =>
    {
        options.Cookie.HttpOnly = false;
        options.Cookie.IsEssential = true;
        options.Events.OnRedirectToLogin = context =>
        {
            context.Response.StatusCode = 401;
            return Task.CompletedTask;
        };
    });

    services.Configure<IdentityOptions>(options =>
    {
        options.Password.RequireDigit = false;
        options.Password.RequiredLength = 6;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
        options.Password.RequireLowercase = false;

        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
        options.Lockout.MaxFailedAccessAttempts = 10;
        options.Lockout.AllowedForNewUsers = true;

        options.User.RequireUniqueEmail = false;
    });

    services.AddControllersWithViews().AddNewtonsoftJson();
    //TODO: Remove when fixed
    //AllowSynchronousIO is required for newtonsoft serialization
    services.Configure<KestrelServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });
    services.Configure<IISServerOptions>(options =>
    {
        options.AllowSynchronousIO = true;
    });

    services.AddResponseCaching();
    services.AddDistributedMemoryCache();

    services.AddSession(options =>
    {
        options.IdleTimeout = TimeSpan.FromMinutes(120);
    });

    services.AddResponseCompression(options =>
    {
        options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
        {
            MediaTypeNames.Application.Octet,
            WasmMediaTypeNames.Application.Wasm,
        });
    });

    //....
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseCors();
    app.UseResponseCompression();
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBlazorDebugging();
    }

    app.UseStaticFiles();


    app.UseCookiePolicy();
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapDefaultControllerRoute();
    });

    app.UseBlazor<Client.Startup>();
}

AuthorizeController.cs

[AllowAnonymous]
[Route("api/[controller]/[action]")]
[ApiController]
public class AuthorizeController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly SignInManager<ApplicationUser> _signInManager;

    public AuthorizeController(UserManager<ApplicationUser> userManager, 
                               SignInManager<ApplicationUser> signInManager)
    {
        _userManager = userManager;
        _signInManager = signInManager;
    }

    [HttpPost]
    public async Task<IActionResult> Login(LoginParameters parameters)
    {
        var user = await _userManager.FindByNameAsync(parameters.Username);
        if (user == null) return BadRequest("User does not exist");
        var singInResult = await _signInManager.CheckPasswordSignInAsync(user, parameters.Password, false);
        if (!singInResult.Succeeded) return BadRequest("Invalid password");

        await _signInManager.SignInAsync(user, true);

        return Ok();
    }
    
    //...

Client-side project HttpClient code

public class AuthorizeApi : IAuthorizeApi
{
    private readonly HttpClient _httpClient;

    public AuthorizeApi(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task Login(string username, string password)
    {
        var stringContent = new StringContent(Json.Serialize(new LoginParameters
        {
            Username = username,
            Password = password
        }), Encoding.UTF8, "application/json");
        var result = await _httpClient.PostAsync("api/Authorize/Login", stringContent);
        if (result.StatusCode == System.Net.HttpStatusCode.BadRequest) 
            throw new Exception(await result.Content.ReadAsStringAsync());
        result.EnsureSuccessStatusCode();
    }

    //...

Request info

image

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:8

github_iconTop GitHub Comments

1reaction
stavroskasidiscommented, May 3, 2019

Turns out, it was a problem with chrome on my PC. I have no idea why it didn’t work, maybe some extension was jamming it. When I used Firefox (or Chrome in Incognito mode) everything worked fine.

I have never seen something like that before. I did a clean re-install of Chrome and now it is working again.

Sorry if I wasted anyone’s time.

0reactions
stavroskasidiscommented, May 2, 2019

I cannot reproduce the issue. I have uploaded a branch of the sample project using preview 4 and everything works fine.

I will keep trying to find the issue, but until then I am gonna close this. I don’t want to waste your time with a possible misconfiguration. I will reopen if needed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

HttpClient doesn't include cookies with requests in Blazor ...
This cookie contains the user's email address, and is used by the user info service to retrieve a more detailed set of user...
Read more >
Securing API calls with Blazor WASM (question) : r/dotnet
I have a Blazor WASM app that calls a .net API all hosted on Azure. ... HTTP requests made from a server does...
Read more >
ASP.NET Core Blazor WebAssembly additional security ...
This article describes additional security scenarios for Blazor WebAssembly apps. Attach tokens to outgoing requests.
Read more >
Blazor: switching Server and WebAssembly at runtime
The problem remains to be solved: when Blazor Server executes client-side code of the application, which makes calls to the API over HTTP,...
Read more >
Authentication with client-side Blazor using WebAPI and ...
In this post, I show how you can build a client-side Blazor app with authentication using WebAPI and ASP.NET Core Identity.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found