.Net Core Cross Site Cookie Not Being Set by Chrome or Firefox - asp.net

I am trying to use a cookie sent from an Asp.Net Core web api site in a cross-site configuratioun. I can see the cookie arrive in the Response, but from what I can tell, it's not being set by either Firefox or Chrome. Either way, it's not being sent back on subsequent requests to the API. When I use Postman, everything works great.
I've tried using .Net Core middleware for authentication cookies with server and app configuration in Startup.cs. But I get the same result if I use the direct approach of appending the cookie to the HTTP response in my controller (shown in the sample code below).
My web site is running out of VS Code from a minimal create-react-app, npm start, localhost port 3000.
My API is running of out Visual Studio 2019, .Net Core 3.1, web api site, port 44302. I've also tried deploying to an Azure app service so that my localhost web site could call a non-localhost API. Cookie still not set or sent.
Question is, how do I get the browser to set and then send the cookie back to the API when developing in localhost (or deployed anywhere, for that matter!)? I've spent hours combing Stack Overflow and other docs for the answer. Nothing has worked. Thanks much for any help!
From Startup.cs. Define CORS policy. Note the allow credentials that pairs with the web site's xhr withCredentials:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder
.SetIsOriginAllowed(host => true)
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
...
}
From my controller endpoint simulating login:
[HttpPost]
public IActionResult FauxLogin(string Email, string Pwd)
{
Response.Cookies.Append("LoginCookie", "123456", new CookieOptions
{
//Domain = ".app.localhost", // some suggest specifying, some suggest leaving empty for default.
Path = "/",
Secure = true,
HttpOnly = false,
SameSite = SameSiteMode.None
});
return Ok(new { success = true });
}
Javascript function calling back to the API:
function callApi() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://localhost:44302/api/account/echo', true);
xhr.withCredentials = true;
xhr.send(null);
}
Response header from dev tools for faux login call. Set Cookie present:
content-type: application/json; charset=utf-8
server: Microsoft-IIS/10.0
set-cookie: LoginCookie=123456; path=/; secure; samesite=none
access-control-allow-origin: http://localhost:3000
access-control-allow-credentials: true
x-powered-by: ASP.NET
date: Sun, 31 Oct 2021 23:27:22 GMT
X-Firefox-Spdy: h2
Request header calling back to API. No cookie.
GET /api/account/echo HTTP/2
Host: localhost:44302
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Origin: http://localhost:3000
Connection: keep-alive
Referer: http://localhost:3000/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site

Related

ASP.NET 4.5 Rest API's work in Unity Android/iOS build but fails with "Unknown error" in Unity WebGL build

I have scoured every possible forum for this and somehow have not gotten my WebGL to consume my ASP.NET 4.5 REST API's.
From what I can tell it is possibly related to WebGL requiring CORS, but even enabling this I cannot get the game to communicate with my API's
So either there's something wrong with the way I have implemented global CORS settings in ASP.NET or something else is breaking.
To be clear these API's are running perfectly well on Android/iOS/Windows builds and even in the editor.
What I have done so far:
Installed the Microsoft CORS build as recommended by Microsoft's documentation relating to it, then added the following code to the WebAPIConfig class in Visual Studio:
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
////new code
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
This is also in my web.config:
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
<add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
<add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
</customHeaders>
</httpProtocol>
I need these settings global so I used the "*" as indicated by the documentation to include all domains, method types, and headers because I use ASP.NET token authentication for my API.
Here is a code snippet that gets the token in the Unity project (just to be clear, this works on other platforms, only throws an error in a WebGL build)
public IEnumerator login()
{
string url = API.ROUTEPATH + API.TOKEN;
WWWForm form = new WWWForm();
form.AddField("grant_type", "password");
form.AddField("username", API.APIUSERNAME);
form.AddField("password", API.APIPASSWORD);
UnityWebRequest uwr = UnityWebRequest.Post(url, form);
uwr.SetRequestHeader("Content-Type", "application/json");
yield return uwr.SendWebRequest();
try
{
if (uwr.isNetworkError)
{
Debug.Log(uwr.error);
}
else
{
APIAuthToken returnauth = JsonUtility.FromJson<APIAuthToken>(uwr.downloadHandler.text);
if (!string.IsNullOrEmpty(returnauth.access_token))
{
API.hasAuth = true;
API.token = returnauth.access_token;
Debug.Log(returnauth.access_token);
}
}
}
catch
{
}
}
uwr.error produces the following, very helpful error: Unknown Error So I'm not even sure if it is CORS related, it's just my best guess based on the research I have done, but even with multiple different implementations of it I still sit with the same error. So if it's not a problem with the API's and with my Unity code please just ignore the ASP.NET code snippet.
cURL - A simple curl -I <endpoint> or curl -X OPTIONS -v <endpoint> can reveal a ton of information about what is happening related to CORS. It can allow you to set different origins, check preflight responses, and more.
"Let's say you have a backend API that uses cookies for session management. Your game works great when testing on your own domain, but breaks horribly once you host the files on Kongregate due to the fact that your API requests are now cross-domain and subject to strict CORS rules."
Is this your problem?
Problably on both sides if things are not set up properly will refuse to send cookies, but its good, its mean you have the control to allow what domains your sessions cookies will be sent to.
So probably you need first to configure the server to allow multiplies origins but make sure to validate the value against a whitelist so that you aren't just enabling your session cookies to be sent to any origin domain.
Example on a Node Express with CORS middleware(game ID 12345) and an origin whitelist below:
express = require('express')
var cors = require('cors')
var app = express()
var whitelist = ['https://game12345.konggames.com'];
var corsOptions = {
credentials: true,
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
};
app.use(cors(corsOptions));
app.options('*', cors(corsOptions)); // Enable options for preflight
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(8080, () => console.log(`Example app listening on port 8080!`))
cURL command to check the headers for an OPTIONS preflight request from an origin in the whitelist array:
curl -X OPTIONS -H"Origin: https://game12345.konggames.com" -v http://localhost:8080/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> OPTIONS / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Origin: https://game12345.konggames.com
>
< HTTP/1.1 204 No Content
< X-Powered-By: Express
< Access-Control-Allow-Origin: https://game12345.konggames.com
< Vary: Origin, Access-Control-Request-Headers
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET,HEAD,PUT,PATCH,POST,DELETE
< Content-Length: 0
< Date: Tue, 24 Sep 2019 22:04:08 GMT
< Connection: keep-alive
<
* Connection #0 to host localhost left intact
instruct the client to include cookies when it makes a cross-domain request,If the preflight response did not include Access-Control-Allow-Credentials: true, or if your Access-Control-Allow-Access is set to a wildcard (*) then the cookies will not be sent and you are likely to see errors in your browser's Javascript console:
Access to XMLHttpRequest at 'https://api.mygamebackend.com' from origin 'https://game54321.konggames.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
Unity's UnityWebRequest and the older WWW classes use XMLHttpRequest under the hood to fetch data from remote servers. Since there is no option to set the withCredentials flag to true, we have to perform a pretty dirty hack when initializing our application in order to turn that on for the appropriate requests.
In your WebGL template or generated index.html:
<script>
XMLHttpRequest.prototype.originalOpen = XMLHttpRequest.prototype.open;
var newOpen = function(_, url) {
var original = this.originalOpen.apply(this, arguments);
if (url.indexOf('https://api.mygamebackend.com') === 0) {
this.withCredentials = true;
}
return original;
}
XMLHttpRequest.prototype.open = newOpen;
</script>
This snippet of code overrides the open method of XMLHttpRequest so that we can conditionally set withCredentials equal to true when desired. Once this is in place, cross-origin cookies should begin working between the Kongregate-hosted iframe domain and the game's backend servers!
info taken from here
also looks nice for this

Spring REST Controller is not responding to Angular request

I have an app to create server certificate requests, just as if one were using java keytool or something. I'm trying to return the created certificate request and the key in a zip file, but for the life of me, I can't get my REST controller to respond to the http request. CORRECTION: The controller responds, but the code within the method is never executed.
The server does receive the request, because my CORS filter is executed. But I have a debug set in the controller method, and it's never triggered. Is the signature of the method correct? I need another set of eyes, please?
Here is my controller code:
#RequestMapping(method = RequestMethod.POST, value = "/generateCert/")
public ResponseEntity<InputStreamResource> generateCert(#RequestBody CertInfo certInfo) {
System.out.println("Received request to generate CSR...");
byte[] responseBytes = commonDataService.generateCsr(certInfo);
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(responseBytes));
System.out.println("Generated CSR with length of " + responseBytes.length);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=certificate.zip")
.contentType(MediaType.parseMediaType("application/zip"))
.contentLength(responseBytes.length)
.body(resource);
}
And here is the Angular request:
generateCertificate(reqBody: GenerateCert) {
let headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.post(this.urlGenerateCert, JSON.stringify(reqBody), {headers: headers}).subscribe(
(data) => {
let dataType = data.type;
let binaryData = [];
binaryData.push(data);
this.certBlob = new Blob(binaryData);
});
return this.certBlob;
}
And finally, the request and response headers I copied from the Network Panel:
Response
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, X-Requested-With, remember-me
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Origin: *
Access-Control-Max-Age: 3600
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Thu, 27 Dec 2018 22:48:00 GMT
Expires: 0
Location: http://localhost:8102/login
Pragma: no-cache
Set-Cookie: JSESSIONID=EDACE17328628D579670AD0FB53A6F35; Path=/; HttpOnly
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Request
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 205
Content-Type: application/json
Host: localhost:8102
Origin: http://localhost:4200
Referer: http://localhost:4200/generateCerts
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36
I really struggled with getting CORS working, so maybe that's interfering with the request? I hate to post all that code unless absolutely necessary. Anybody got any ideas?
Listing of request/response headers lack information on URL, method and most important response status code.
Seeing Location: http://localhost:8102/login among response headers I can guess that it could be 401 Unauthorized or anything else that redirects to the login page. Hence, if there is an auth filter in the filter chain, it may be a culprit.
The following request headers
Host: localhost:8102
Origin: http://localhost:4200
suggests that you are doing CORS and the CORS filter may be involved indeed and fulfill response before the request gets routed to the controller. I suggest setting a breakpoint into the CORS filter (and into others if any) and debug it to the point where the response is returned.
define a proxy.conf.json
{
"/login*": {
"target":"http://localhost:8080",
"secure":false,
"logLevel":"debug"
}
}
now in your package.json
"scripts": {
"start":"ng serve --proxy-config proxy.config.json"
}
I think there is issue while getting connection in both webapp.please try .
When Angular encounters this statement
this.http.post(url,body).subscribe(data => # some code
);
It comes back immediately to run rest of the code while service continues to execute. Just like Future in Java.
Here if you
return this.cert;
You will not get the value that may eventually get populated by the this.http service. Since the page has already rendered and the code executed. You can verify this by including this within and outside the Observable.
console.log(“Inside/outside observable” + new Date().toLocalTimeString());
Thanks to everyone who contributed. I discovered the error was due to the headers of my controller method. After changing them, the method was invoked properly. This is what worked:
#RequestMapping(method = RequestMethod.POST, path = "/generateCert",
produces = {MediaType.APPLICATION_OCTET_STREAM_VALUE}, consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<byte[]> generateCert(#RequestBody CertInfo certInfo) {
byte[] responseBytes = commonDataService.generateCsr(certInfo);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE)
.contentLength(responseBytes.length)
.body(responseBytes);
}

Cookies not being set by IIS in HTTP header

I use ASP.NET forms authentication that seems to work ok online but not in my development environment for Internet Explorer, Firefox, and Chrome. As far as I can see IIS is not sending the Set-Cookie HTTP header when a page is being requested:
GET http://127.0.0.1:81/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: nb-NO
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:81
DNT: 1
Connection: Keep-Alive
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 08 Oct 2014 19:24:58 GMT
Content-Length: 13322
I've tried adding 127.0.0.1 www.example.com to the \Windows\System32\drivers\etc\hosts file and accessing http://www.example.com:81 instead, but that has no effect. Here are my web.config settings:
<!-- ASP.NET forms authentication is enabled by default -->
<authentication mode="Forms">
<!-- Set the page to redirect to when the user attempts to access a restricted resource -->
<forms loginUrl="~/Account/Login.aspx" timeout="2880" />
</authentication>
I've found a work-around by always setting a dummy cookie if no cookies are sent inside the ASP.NET web page:
/// <summary>
/// Force the browser to use cookies if none are in use.
/// Sets an empty cookie.
/// </summary>
void ForceCookiesIfRequired()
{
if (Request.Cookies == null || Request.Cookies.Count == 0)
{
// No cookies, so set a dummy blank one
var cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, String.Empty) { Expires = DateTime.Now.AddYears(-1) };
Response.Cookies.Add(cookie1);
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// Force the use of cookies if none are sent
ForceCookiesIfRequired();
}
I never had todo this before, has some Microsoft patch or upgrade broken ASP.NET forms authentication? One way to check your own solutions is to clear the cookie I guess. This forces the Set-Cookie HTTP header to be sent by the IIS server.

Kendo upload not working in IE 8

Context
I am using asynchronous Kendo UI Upload (docs) in my application with HTML frontend and Java servlet at server side. When servlet returns nothing in response, it’s working perfectly. Uploading triggers progress change to “Done” on success, complete callback method is called.
Problem
I need to return some data such as GUID="SDR2334" from the server on successful upload. When I send response from the servlet, Kendo UI Upload control does not work/render as expected/as shown in Kendo demo site at all.
progress not getting changed to “Done” when uploading
complete method not being called
Attempted solution
I tried to add GUID with response header instead of response body. Still it’s not working.
Code I used
<form method="post" action="submit" style="width:45%">
<div class="demo-section">
<input name="files" id="files" type="file" />
</div>
</form>
$("#files").kendoUpload({
async: {
saveUrl: "http:111.11.11.111/fileupload",
autoUpload: false
},
multiple: false,
showFileList: true,
upload: function (e) {
e.data = { sessionid: CurrentSession.sessionId };
},
complete: function (e) {
alert(e.data);
$(".k-widget.k-upload").find("ul").remove();
}
});
Request header
Key Value
Request POST /services/fileUploadWithoutResponse HTTP/1.1
Accept application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer http://111.11.11.11:8090/WebClient/
Accept-Language en-US
User-Agent Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)
Content-Type multipart/form-data; boundary=---------------------------7de38b1219073a
Accept-Encoding gzip, deflate
Host 172.16.17.100:8090
Content-Length 114034
DNT 1
Connection Keep-Alive
Cache-Control no-cache
Response header
Key Value
Response HTTP/1.1 200 OK
Server Apache-Coyote/1.1
X-Powered-By Servlet 2.5; JBoss-5.0/JBossWeb-2.1
Access-Control-Allow-Origin *
guId f6ac7203-5bd6-433b-a632-548ca5b048cf
Content-Type application/json;charset=utf-8
Content-Length 0
Date Fri, 03 Jan 2014 13:00:19 GMT
Notice the guId header here.
I made it by setting the response content-type to text/plain.
Also I realized I need success event instead of complete event to get the contents of response body (e.response).

SignalR routing issue, get 200 ok but response empty

I have an existing MVC application which I am integrating a hub into, now I have setup the hub like so:
routeTable.MapHubs("myapp/chat/room", new HubConfiguration { EnableCrossDomain = true, EnableDetailedErrors = true, EnableJavaScriptProxies = true });
Then in the clientside I am connecting like so:
var connection = $.hubConnection(SystemConfiguration.ServiceUrl + "/myapp/chat/room", { useDefaultPath: false });
var hub = this.Connection.createHubProxy("ChatHub"); // Same name as on the hub attribute
connection.start().done(function(){ /* do stuff */});
Then I see the HTTP Request like so:
http://localhost:23456/myapp/chat/room/negotiate?_=1374187915970
Response Headers
Access-Control-Allow-Cred... true, true
Access-Control-Allow-Head... content-type, x-requested-with, *
Access-Control-Allow-Meth... GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Orig... http://localhost:34567, http://localhost:34567
Access-Control-Max-Age 10000
Cache-Control no-cache
Content-Length 420
Content-Type application/json; charset=UTF-8
Date Thu, 18 Jul 2013 22:52:18 GMT
Expires -1
Pragma no-cache
Server Microsoft-IIS/8.0
X-AspNet-Version 4.0.30319
X-Content-Type-Options nosniff
Request Headers
Accept application/json, text/javascript, */*; q=0.01
Accept-Encoding gzip, deflate
Accept-Language en-US,en;q=0.5
Content-Type application/x-www-form-urlencoded; charset=UTF-8
Host localhost:23456
Origin http://localhost:34567
Referer http://localhost:34567/myapp/chat?chatId=1764a2e3-ff6f-4a17-9c5f-d99642301dbf
User-Agent Mozilla/5.0 (Windows NT 6.2; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0
The response though contains no body, its got a 200 status though... I am debugging on the server and the hub methods are never hit. The only non standard thing in this scenario is that I have a custom CORS HttpModule which intercepts traffic and appends the CORS required headers, as you can see in the response, so not sure if this confuses SignalR's CORS support in some way. Anyway I can see the HttpModule being hit so it goes past there fine, but is somehow lost between there and the hub.
Tried googling but not much info on this topic...
The issue seems to be down to my CORS handling at HttpModule level, it must somehow conflict with SignalR... if I put a check in the module to see if the URL contains "chat/room" and just ignore the request if needed it then works fine, however it feels like a hack, but at least it works now.

Resources