Chris Davis
Chris Davis

Reputation: 81

Accessing session information from another tab and site

I have inherited maintenance of a PHP website that is mysite.com and an ASP.NET website that is shop.mysite.com. The client wants a single sign-on for both sites. The solution I am pursuing is to create a function on the ASP.NET site that returns the necessary information to the PHP site.

The ASP.NET function is called "IsUserLoggedIn" that returns a JSON object with the username and an array of roles.

Here is a condensed version of that function:

        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> IsUserLoggedIn()
        {
            try
            {
                    if (HttpContext.Request.Headers["api-key"][0] != configuration["ApiKey"])
                    {
                        HttpContext.Response.StatusCode = 200;
                        return Json(new { authenticated = "no key", roleId = "" });
                    }

                    if (HttpContext.User.Identity.IsAuthenticated)
                    {
                        var user = await userOperations.GetUser.Execute(HttpContext.User);
                        var companyUser = await companyRepository.GetUser(user.Id, user.ActiveCompanyId);

                        var roles = new List<String>();
                        if (companyUser.Company.CustomerRoles != null)
                        {
                            roles.AddRange(companyUser.Company.CustomerRoles);
                        }

                        if (companyUser.UserCompanies != null)
                        {
                            foreach (var company in companyUser.UserCompanies)
                            {
                                if (company.CustomerRoles != null)
                                {
                                    roles.AddRange(company.CustomerRoles);
                                }
                            }
                        }

                        HttpContext.Response.StatusCode = 200;
                        return Json(new { authenticated = "yes", User = user, roleId = roles });
                    }
                    else
                    {
                        HttpContext.Response.StatusCode = 200;
                        return Json(new { authenticated = "not auth", roleId = "" });
                    }
            }
            catch (AuthenticationException e)
            {
                HttpContext.Response.StatusCode = 200;
                return Json(new { authenticated = "error", roleId = "", e });
            }
        }

When I am logged into shop.mysite.com and open another tab and go to shop.mysite.com/User/IsUserLoggedIn (with the api-key in the request header), it returns a JSON object as expected:

{
  "authenticated": "yes",
  "user": {
    "id": 17085,
    "username": "cdavis",
    "verified": true,
    "activeCompanyId": "TEST001"
  },
  "roleId": [
    "NGFE"
  ]
}

However, when I try to get the data using fetch in JavaScript, it returns a JSON object showing there is no user authorized. Here is my JavaScript code running in mysite.com:

    async function checkUserLoggedIn() {
        try {
            const response = await fetch('https://' + custom_script_vars.api_url + '/User/IsUserLoggedIn', {
                method: 'GET',
                headers: {
                    'api-key': custom_script_vars.api_key,
                },
                credentials: 'include',
            });
            
            console.log('Response status:', response.status);

            if (response.ok) {
                const jsonData = await response.json();
                console.log('User logged in:', jsonData);

                const phpEndpoint = custom_script_vars.theme_directory + '/set-session.php';

                // Use AJAX or another fetch to pass the JSON data to your PHP method
                await fetch(phpEndpoint, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ companyResourcesSessionUserLoggedIn: jsonData }),
                });
                console.log('User logged in:', jsonData);
            } else {
                console.error('Failed to check user login status:', response.status);
            }
        } catch (error) {
            console.error('Error during user login check:', error);
        }
    }

The JSON object returned by the JavaScript fetch:

{
  "authenticated": "not auth",
  "roleId": ""
}

Am I just trying to do something that is not allowed? I have tried it in both Chrome and Edge with the same behavior in each.

I assume that fetch in JavaScript is not just like "opening a tab in the background". I'd love any alternatives if you have done something similar.

Upvotes: 0

Views: 39

Answers (1)

Chris Davis
Chris Davis

Reputation: 81

It was the SameSite=Lax that was preventing the cookie from being read. I added code in my ASP.NET project to change the cookie to SameSite=None.

public class SameSiteMiddleware
{
    private readonly RequestDelegate _next;

    public SameSiteMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        context.Response.OnStarting(state =>
        {
            var httpContext = (HttpContext)state;

            // Check if the request is secure (HTTPS)
            bool isHttps = httpContext.Request.IsHttps;

            // Set SameSite and Secure attributes for cookies
            var setCookieHeaders = httpContext.Response.Headers["Set-Cookie"];
            if (setCookieHeaders.Count > 0)
            {
                var updatedCookies = new List<string>();
                foreach (var responseCookie in setCookieHeaders)
                {
                    if (responseCookie.IndexOf("CompanyAuthenticationScheme", StringComparison.OrdinalIgnoreCase) != -1)
                    {
                        if (responseCookie.IndexOf("SameSite", StringComparison.OrdinalIgnoreCase) == -1)
                        {
                            updatedCookies.Add(responseCookie + "; SameSite=None");
                        }
                        else
                        {
                            updatedCookies.Add(responseCookie.Replace("SameSite=Lax", "SameSite=None", StringComparison.OrdinalIgnoreCase));
                        }

                        if (isHttps && responseCookie.IndexOf("Secure", StringComparison.OrdinalIgnoreCase) == -1)
                        {
                            updatedCookies.Add(responseCookie + "; Secure");
                        }
                    }
                    else
                    {
                        updatedCookies.Add(responseCookie);
                    }
                }

                httpContext.Response.Headers["Set-Cookie"] = updatedCookies.ToArray();
            }

            return Task.CompletedTask;
        }, context);

        await _next(context);
    }  }

Added to my Configure method (after app.UseAuthentication()):

         app.UseMiddleware<SameSiteMiddleware>();

This article was very helpful in diagnosing my issue: https://andrewlock.net/making-authenticated-cross-origin-requests-with-aspnetcore-identity/

Upvotes: 0

Related Questions