Search code examples
ioscookiessignalrcross-domain.net-7.0

Cross domain cookie authentication failing on IOS (.net 7)


I'm using signalr to implement a chat service. On form submission in the chat it hits a controller that authenticates the user and adds a cookie that I then use to keep the chat current via signalr. This works perfectly on all browsers except for the ones running on IOS. The authentication is done to a different domain to that the chat window is displaying on.

When I inspect the network tab and compare, I see the "aspnetcore.cookies" response to the authentication post in the browsers that work but not on IOS.

I have tried various settings in start up and this is what is currently in place

 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(options =>
                {
                    options.ExpireTimeSpan = TimeSpan.FromDays(1);
                    options.SlidingExpiration = true;
                    options.LoginPath = "/Identity/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
                    options.LogoutPath = "/Identity/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
                    options.AccessDeniedPath = "/Identity/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /A
                    options.Cookie.SameSite = SameSiteMode.None;
                    options.Cookie.IsEssential = true;
                    
                });
            services.AddSession(
                options =>
                {
                    options.Cookie.SameSite = SameSiteMode.None;
                });
 services.AddAuthorization(options =>
            {
                options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
            });
            services.AddSignalR(hubOptions =>
                {
                    hubOptions.EnableDetailedErrors = true;
                });
 app.UseRouting();
            app.UseCookiePolicy(new CookiePolicyOptions
            {
                MinimumSameSitePolicy = SameSiteMode.None
            });
            app.UseAuthentication();
            app.UseAuthorization();

Solution

  • So this turned out to be IOS browsers not playing nicely with authentication cookies from a different domain. I was able to verify this by turning off the setting in Safari settings that prevent tracking cookies.

    To work around this I found two ways. Switch to JWT authentication and then pass the token in the connection action. I saved the token in local storage and then would pass it on each call. Something like this :

        fetch('/api/account/login', {
    method: 'POST',
    body: JSON.stringify({
        username: 'username',
        password: 'password'
    }),
    headers: {
        'Content-Type': 'application/json'
    }}).then(response => response.json()).then(data => {
    localStorage.setItem('token', data.token);}).catch((error) => {
    console.error('Error:', error);});
    

    I would then use this token in the connection action as follows:

    var connection = new signalR.HubConnectionBuilder()
    .withUrl("/chatHub", { accessTokenFactory: () => localStorage.getItem('token') })
    .build();
    

    How secure this is I can't attend to but thought I would post this in case it helps someone. I ended up going a different way where I removed authentication altogether. But my use case is unique as I am able to control the authentication on the site the chat service is hosted on.

    I hope this helps someone.