A customer will link to one of our webpages on their site: customer.site/links.html
A person clicks that link and gets sent to our.site/webapp/handlerequest.aspx?someparam=somevalue
The value of someparam is set in a cookie with SameSite=Strict and then uses a 302 redirect to another page on the same domain:
Request URL: https://our.site/webapp/handlerequest.aspx?someparam=somevalue
Request Method: GET
Status Code: 302
Remote Address: ...
Referrer Policy: strict-origin-when-cross-origin
cache-control: private
content-length: ...
content-type: text/html; charset=utf-8
date: ...
location: /webapp/someotheraction
server: Microsoft-IIS/10.0
set-cookie: someparam=somevalue; expires=Thu, 17-Mar-2022 14:41:13 GMT; path=/; secure; HttpOnly; SameSite=Strict
strict-transport-security: max-age=31536000
x-frame-options: SAMEORIGIN
The browser does NOT include this cookie on the 302 redirect to /webapp/someotheraction.
This only starting happening when we specifically change our code to set this cookie to SameSite=Strict.
This occurs in Chrome, Firefox, Edge, and IE (old IE)
Is this on purpose? Why? Since we are going from one request on the domain to another request in the same domain, shouldn't the SameSite=Strict cookies be included? Does this have anything to do with the referer policy defaulting to strict-origin-when-cross-origin? https://www.w3.org/TR/referrer-policy/ doesn't say anything about cookies
This is a cross-site request because the initial navigation was cross-site (from customer.site to our.site). Strict cookies are never sent on cross-site requests. It doesn't matter that the request gets redirected (in this case, to another URL on our.site), just the fact that the user clicked on a cross-site link means the request is cross-site.
As for why this is the case, it's because the origin responsible for initiating the navigation is important in preventing cross-site request forgery (CSRF). Imagine if https://evil.site had a link to https://bank.site/transfer-funds which redirects to https://bank.site/transact. We wouldn't want Strict cookies to be send to the /transact endpoint after the redirect, even if it was redirected to by the same site, because the initiating origin is cross-site.
I am trying to implement an authentication service deployed in a different HTTP server from the one serving my login page.
The following diagram depicts my setup:
On step #1 my browser makes an HTTP GET request to fetch the login page. This is provided as the HTTP response in #2. My browser renders the login page and when I click the login button I send an HTTP POST to a different server (on the same localhost). This is done in #3. The authentication server checks the login details and sends a response that sets the cookie in #4.
The ajax POST request in #3 is made using jQuery:
$.post('http://127.0.0.1:8080/auth-server/some/path/',
{username: 'foo', password: 'foo'},
someCallback);
The authentication service's response (assuming authentication was successful) has the following header:
HTTP/1.1 200
Set-Cookie: session-id=v3876gc9jf22krlun57j6ellaq;Version=1
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: origin, content-type, accept, authorization
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 21 Nov 2016 16:17:08 GMT
So the cookie (session-id) is present in the HTTP response in step #4.
Now if the user tries to login again I would like the authentication service to detect that. To test that scenario I press the login button again in order to repeat the post in #3. I would have expected the second request to contain the cookie. However the second time I press the login button, the request header in the post sent out in #3 does not contain the cookie.
What I have discovered is that for the post in #3 to contain the cookie, I have to do it like this:
$.ajax({
type: 'post',
url: 'http://127.0.0.1:8080/auth-server/some/path',
crossDomain: true,
dataType: 'text',
xhrFields: {
withCredentials: true
},
data: {
username : 'foo',
password : 'foo',
},
success: someCallback
});
Why would that be necessary? MDN states that this is only required for cross-site requests. This SO post also uses xhrFields but only in relation to a cross-domain scenario. I understand that my case is not cross-domain as both the page that serves the script is on localhost, and the page to where the ajax request is sent is on the same host. I also understand that cookie domains are not port specific. Moreover, since my cookie did not explicitly specify a domain, then the effective domain is that of the request meaning 127.0.0.1 which is identical the second time I send the POST request (#3). Finally, the HTTP reponse on #4 already includes Access-Control-Allow-Origin: * which means that the resource can be accessed by any domain in a cross-site manner.
So why did I have to use the xhrFields: {withCredentials: true} to make this work?
What I understand is that setting Access-Control-Allow-Origin: * is simply enabling cross-site requests but that in order for cookies to be sent then the xhrFields: {withCredentials: true} should be used regardless (as explained in MDN section on requests with credentials). Moreover, I understand that the request is indeed cross-site since the port number is important when deciding whether a request is cross-site or not. Whether the domain of a cookie includes ports is irrelevant. Is this understanding correct?
update
I think this is explained very clearly in this answer, so maybe this question should be deleted.
All parts of the origin must match the host(ajax target) for it to be considered same-origin. The 3 part of the origin https://sales.company.com:9443 for example includes:
protocol/scheme (https - won't match http)
hostname (sales.company.com - won't match subdomain.sales.company.com)
port (9443 - won't match 443)
see https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
Test Plan:
HTTP Cookie manager
1. Open Login Page (get) - (send cookie with with JSESSIONID)
2. Log in (post) - (send cookie with with LtpaToken2, receive cookie with JSESSIONID)
3. Create new app (post)
Path contains ;jsessionid=${jsessionid} in Log in (post)
I have strange behavior. I get login page in response in post methods, thus request sent without exceptions but didn`t log in.
Also I noticed there are different JSESSIONID for every operation.
For example
step1: Request - no cookies, Sampler result - Set-Cookie: JSESSIONID=00005VALCRoQLgAgEsC_CIOVc5x:-1; Path=/
step2: Request - Cookie Data:JSESSIONID=00005VALCRoQLgAgEsC_CIOVc5x:-1, Sampler result - Set-Cookie: JSESSIONID=0000OnEiWZdVvxMa8n0Sew2_4Pl:-1; Path=/
Step3: Request - Cookie Data:JSESSIONID=0000OnEiWZdVvxMa8n0Sew2_4Pl:-1 , Sampler result - Set-Cookie: JSESSIONID=0000xbnlZFrZuYSdaY12--sdgg1:-1; Path=/
What is wrong with my script? I need to log in portal.
Thanks in advance.
Not actual anymore. HTTP Cookie Manager manage all session cookies by default (Jsession and ldap). I set following parameters:
Clear cookie each iteration =Yes
Cookie Policy = Compatible
Implementation = HC4CookieHandler
I am signing users in using their Google+ account. I sign them in and grab basic information and store it in the database. In this process, I store the access_token in the session and move on.
However, today I am trying to write a script that allows me to post to their 'moments' on Google+ using their in session access_token.
I am getting an error and the response looks like:
{ "error": { "errors": [ { "domain": "global", "reason": "authError", "message": "Invalid Credentials", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Invalid Credentials" } }
I am not sure why this is happening, below is the code I am using to make the request (it is in ColdFusion script, but you should be able to see the principles behind it even if you do not know this syntax).
local.http = new http();
local.http.setMethod("post");
local.http.setCharset("utf-8");
local.http.setUseragent(cgi.http_user_agent);
local.http.setResolveurl(true);
local.http.setTimeout(20);
local.http.setUrl("https://www.googleapis.com/plus/v1/people/" & session.user.sourceid & "/moments/vault");
local.target = {};
local.target["kind"] = "plus##moment";
local.target["type"] = "http://schemas.google.com/AddActivity";
local.target["description"] = params.pin["description"];
local.target["image"] = session.user.image;
local.target["name"] = params.pin["title"];
local.target["url"] = URLfor(route="pinShow", key=obfuscateParam(pin.id), onlyPath=false);
local.target["latitude"] = session.user.latitude;
local.target["longitude"] = session.user.longitude;
local.http.addParam(type="formField", name="target", value=serialize(local.target));
local.http.addParam(type="formField", name="kind", value="plus##moment");
local.http.addParam(type="formField", name="type", value="http://schemas.google.com/AddActivity");
local.http.addParam(type="formField", name="access_token", value=session.access_token);
local.result = local.http.send().getPrefix();
As you can see, it all seems straight-forward.
I have test this straight-after signing in and despite that, it says the token is invalid in the header response:
HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer realm="https://www.google.com/accounts/AuthSubRequest", error=invalid_token Content-Type: application/json; charset=UTF-8 Content-Encoding: gzip Date: Wed, 11 Sep 2013 13:12:28 GMT Expires: Wed, 11 Sep 2013 13:12:28 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Content-Length: 162 Server: GSE Alternate-Protocol: 443:quic
Does anyone have any idea why this is happening and how to solve it?
I am not using any kind of library, as there isn't one for ColdFusion. In addition, I didn't want to, as my needs are only very basic and I wanted to see how it all worked.
Am I missing something obvious here?
Any help would be greatly appreciated as it's driving me nuts!
Thanks,
Mikey.
PS - I have removed the app from my account, cleared all cookies and sessions and then signed in again granting all permissions, so it seems that side of it has been eliminated.
UPDATE 1:
After some light shone from other users here, it turns out I should be posting a JSON response in the HTTP body, to make the request. So I changed my code to this:
local.request = {}
local.request["kind"] = "plus##moment";
local.request["type"] = "http://schemas.google.com/AddActivity";
local.request["target"] = {};
local.request.target["kind"] = "plus##itemScope";
local.request.target["type"] = "http://schemas.google.com/AddActivity";
local.request.target["description"] = params.pin["description"];
local.request.target["image"] = session.user.image;
local.request.target["name"] = params.pin["title"];
local.request.target["url"] = URLfor(route="pinShow", key=obfuscateParam(pin.id), onlyPath=false);
local.request.target["latitude"] = session.user.latitude;
local.request.target["longitude"] = session.user.longitude;
local.http = new http();
local.http.setMethod("post");
local.http.setCharset("utf-8");
local.http.setUseragent(cgi.http_user_agent);
local.http.setResolveurl(true);
local.http.setTimeout(20);
local.http.setUrl("https://www.googleapis.com/plus/v1/people/" & session.user.sourceid & "/moments/vault?debug=true&fields=kind%2Ctype%2Cdescription%2Cimage%2Curl&key={GOOLE_API_KEY}" );
local.http.addParam(type="header", name="Content-Type", value="application/json");
local.http.addParam(type="header", name="Authorization", value="Authorization: Bearer " & session.access_token);
local.http.addParam(type="body", value=serializeJSON(local.request));
local.result = local.http.send().getPrefix();
However, now I get another error (401 unauthorized):
{ "error": { "errors": [ { "domain": "global", "reason": "required", "message": "Login Required", "locationType": "header", "location": "Authorization" } ], "code": 401, "message": "Login Required" } }
Does anybody know how I can pass the access_token using my new method above?
UPDATE 2
It has been highlighted to me that this could originate from my original OAuth 2 process. I have removed the app from my Google+ account and started the confirmation / signin process again. Here is the generated URL:
https://accounts.google.com/ServiceLogin?service=lso&passive=1209600&continue=https://accounts.google.com/o/oauth2/auth?response_type%3Dcode%26scope%3Dhttps://www.googleapis.com/auth/userinfo.email%2Bhttps://www.googleapis.com/auth/userinfo.profile%2Bhttps://www.googleapis.com/auth/plus.login%26redirect_uri%3Dhttp://{MY_DOMAIN}.com/oauth/google/?do%253Dredirect%26state%3D2BFFBC14-29F9-4488-ABBF661C0E9E53DB%26client_id%3D{MY_CLIENT_ID}%26hl%3Den-GB%26from_login%3D1%26as%3D-593afcc82466f5f<mpl=popup&shdf=CnALEhF0aGlyZFBhcnR5TG9nb1VybBoADAsSFXRoaXJkUGFydHlEaXNwbGF5TmFtZRoIVW5pYmFuZHMMCxIGZG9tYWluGghVbmliYW5kcwwLEhV0aGlyZFBhcnR5RGlzcGxheVR5cGUaB0RFRkFVTFQMEgNsc28iFOyetn24YRlbdWKLAKGXFCH5C1p9KAEyFPquOHBH18K6iV1GTAg_P9zB2x60&sarp=1&scc=1
Am I missing something here? Is it a scope that is missing that should allow me to post to their AddActivity stream?
UPDATE 3
My OAuth login URL (I've tried to split on to new lines to improve readability):
https://accounts.google.com/o/oauth2/auth? scope=
https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+
https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+
https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fplus.login&
request_visible_actions=
https%3A%2F%2Fschemas.google.com%2FAddActivity& state=
65B4A4D1-0C49-4C65-9B46163E67D88EAD& redirect_uri=
http%3A%2F%2FXXXXXXX.com%2Foauth%2Fgoogle%2F%3Fdo%3Dredirect&
response_type= code& client_id= XXXXXXXX.apps.googleusercontent.com
And on the permissions screen on Google+, this is what I see:
When I try to post an addActivity now, I get a bad request error as before.
The error JSON returned:
{ "error": { "errors": [ { "domain": "global", "reason": "invalid", "message": "Invalid Value" } ], "code": 400, "message": "Invalid Value" } }
The header returned:
HTTP/1.1 400 Bad Request Content-Type: application/json; charset=UTF-8 Content-Encoding: gzip Date: Fri, 13 Sep 2013 11:38:20 GMT Expires: Fri, 13 Sep 2013 11:38:20 GMT Cache-Control: private, max-age=0 X-Content-Type-Options: nosniff X-Frame-Options: SAMEORIGIN X-XSS-Protection: 1; mode=block Content-Length: 123 Server: GSE Alternate-Protocol: 443:quic
There's a definite problem with at least one header:
local.http.addParam(type="header", name="Authorization", value="Authorization: Bearer " & session.access_token);
You will have authorization in there twice, should be.
local.http.addParam(type="header", name="Authorization", value="Bearer " & session.access_token);
What's probably happening with the people.get call is it is picking up the API key and returning publicly rather than as the signed in user.
As Brett says though, to write an app activity you need to a &request_visible_actions=http://schemas.google.com/AddActivity to the auth URL you are sending users to in the first place (if not using the JS sign in button), in order to have the user approve you to write that kind of action (it's like another type of scope).
Although I'm not completely familiar with ColdFusion, it looks like you may be mixing two concepts in here that can't be mixed. The access_token is usually specified either in the HTTP header, or as a URL query parameter. It is not typically set as a POST parameter, and that may be part of the problem.
If done in a header, the HTTP header should be
Authorization: Bearer 1/fFBGRNJru1FQd44AzqT3Zg
and if done with the URL query parameter, your URL might need to look something like
https://www.googleapis.com/plus/v1/people/123456789012345/moments/vault?access_token=1/fFBGRNJru1FQd44AzqT3Zg
See https://developers.google.com/accounts/docs/OAuth2UserAgent#callinganapi for details
Not sure if this is related or not, although it might be, but I'm not sure if the body of your POST is correct either. You're not setting a content-type for the body, and I don't know if CF will use something like "application/www-form-url-encoded" or "multipart/form-data", neither of which is valid. The body should be "application/json".
Update
So given your changed code, you probably want the setUrl line to be something like
local.http.setUrl("https://www.googleapis.com/plus/v1/people/" & session.user.sourceid & "/moments/vault?access_token=" & session.access_token );
The URL I provided above gives the definitive documentation for OAuth2 at Google. I'm not sure what other parameters you were trying to add to the URL, or why, but if they are essential you can add them after the access token in this way.
How exactly are you getting the Access Token? Are you using OAuth?
Are you using new Access Token each time? Not the one stored once in the DB? You need to keep in mind that tokens can expire quite quickly.
This bit:
"reason": "authError", "message": "Invalid Credentials", "locationType": "header", "location": "Authorization"
may mean that your Authorization header is incorrect - not existing I suspect, as I can't see sending any signature in the bit of code you provided.
I haven't used the G+ API, but it seems you need to use proper OAuth 2.0 to get any data:
https://developers.google.com/+/api/latest/people/get#examples
There's a CF library for OAuth here http://oauth.riaforge.org/
UPDATE:
If you go here: https://developers.google.com/+/api/latest/moments/insert#try-it and try running the example (using "me") as userid you'll see that the request looks like this:
POST https://www.googleapis.com/plus/v1/people/me/moments/vault?debug=true&key={YOUR_API_KEY}
Content-Type: application/json
Authorization: Bearer ya29.XXXXXXXXXXXXXXXXXXXXXXXXXXXX
You need to provide the "Authorization" header.
Edited in response to update 2
This sounds like a problem with setting up the OAuth flow. You appear to be missing the Based on your OAuth generated URL, you are missing the request_visible_actions parameter either being missing or malformed.
Add the following query parameter to your OAuth URL and this should start working:
&request_visible_actions=http%3A%2F%2Fschemas.google.com%2FAddActivity
Another problem with your request is that you should remove the userinfo.profile scope from your request. That scope is not necessary because you have plus.login included and can result in some weird issues.
Lastly, ensure that your page at the target URL has the minimal level schema.org metadata that is required for the AddActivity moment type. This isn't your problem now, but could be the next problem you run into.
I'm trying to connect to a https web service (not .NET as far as I know).
I can't control the other side in any way, I just got some standards and a wsdl to operate
with it.
I have created at first the client using Add Service Reference, tried some things until I get through some problems, where one most serious was that I couldn't add the Authentication header to the message which was resulting in fail.
Added the service using old Add Web Reference and seemed more easily managed and appropriate, using a partial class and override the GetWebRequest, I added this code so I can preauthenticate with the service and add the security header, which they don't mention in the wsdl link. I know that it is not mandatory for services to tell this but it would be nice my Web Service creators fellow developers.
protected override WebRequest GetWebRequest(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(uri);
if (PreAuthenticate)
{
NetworkCredential networkCredentials = Credentials.GetCredential(uri, "Basic");
if (networkCredentials != null)
{
byte[] credentialBuffer = new UTF8Encoding()
.GetBytes(networkCredentials.UserName + ":" + networkCredentials.Password);
request.Headers["Authorization"] = "Basic" + Convert.ToBase64String(credentialBuffer);
}
else
{
throw new ApplicationException("No network credentials");
}
}
return request;
}
To call the service I added this code:
using (Service client = new Service()) // autogenerated Service class
{
client.EnableDecompression = true;
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("test1", "test1");
Uri uri = new Uri(client.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
client.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
client.PreAuthenticate = true;
// Make the web service call.
Request req = new Request { UserName = "test2", Password = "test2"; // an object created from autogenerated code
RequestResult result = client.processMessage(req); // autogenerated code
}
While testing this call and checking with fiddler my request. I see 2 calls a keep alive call with these header, nothing special.
CONNECT server:443 HTTP/1.1
Host: server
Connection: Keep-Alive
Sending 570 returning a 200 result.
HTTP/1.1 200 Connection Established
FiddlerGateway: Direct
StartTime: 00:05:13.743
Connection: close
And the call with the data sending 571 result 500 error:
POST /host/Service HTTP/1.1
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 2.0.50727.5448)
Authorization: BasicdXNlcOTc3MzQyMGTDFTR4dftfrdg5 // changed this hash for security reasons
VsDebuggerCausalityData: // Removed this hash for security reasons
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Host: server-host-url
Content-Length: 7238
Expect: 100-continue
Accept-Encoding: gzip
Connection: Keep-Alive
The error exception in .NET client:
Error on verifying message against security policy Error code:1000
As you see the Authorization header exist. I also tried with adding a space after Basic you can see above where exactly in the overriden method, and seemed fiddler recognized it better and also decoded the username:password header.
This results into that response:
HTTP/1.1 500 Internal Server Error
Date: Sat, 21 Apr 2012 21:05:22 GMT
Server: Oracle-Application-Server-11g
X-Powered-By: Servlet/2.5 JSP/2.1
X-Cnection: close
Transfer-Encoding: chunked
Content-Type: text/xml;charset="utf-8"
Content-Language: en
X-Pad: avoid browser bug
Set-Cookie: BIGipServerpoolXoas243_254_9999=437682499.99988.0000; path=/
The strange thing I wonder first is if the first call should be preauthenticated, the handshake keep alive one? This 500 error I know that causes when authentication header is not present, but mine is. Preauthentication is what I need to know how it should happen and I guess it's not working if it should appear in the 1st message.
Another strange thing is that if I change the 2 pairs of passwords opposite, I see on fiddler that I get 3 messages, 1 with the handshake and result in 200 and "TWO" others with 401 Authorization Required.
This drives me crazy. Any help appreciated to save my soul.
Thank you!