Reputation: 81
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
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