Samesite=Strict cookies are not included in 302 redirects when user clicks link from a different domain - cookies

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.

Related

Is cookie with longer domain are listed before cookie with shorter domain?

Say for example I had an application sending the following HTTP headers to set to cookie named "key1":
Set-cookie: key1=111; Domain=cc.net
Set-cookie: key1=222; Domain=bb.cc.net
Set-cookie: key1=222; Domain=aa.bb.cc.net
If I access aa.bb.cc.net on the server, so I have three cookies named "key1",but How are these three cookies arranged? Is cookie with longer domain are listed before cookie with shorter domain?
What is the specification of rfc?

Cookie is not stored in the brower, cross domain GET request

I have a domain (say cookiebaker.com) that provides files using GET requests. Whenever a request is made the cookiebaker server adds a set-cookie header to the file response.
Here is an example header (Max-Age is set for 1 month in the future):
set-cookie: cookie_name=cookie_value; Max-Age=2592000; secure; HttpOnly; SameSite=Lax
Now when I call cookiebaker.com from a different domain (say munchies.com) I can see the set-cookie header in the GET response, but munchies.com does not store the cookie. I don't see the cookie in dev tools, and it is not uploaded in subsequent requests.
I am aware that I have to set the "withCredentials" flag to true when performing the GET request, but this didn't help in my case.
Here's my stripped down munchies.com code:
let request = new XMLHttpRequest();
request.open('GET', "https://cookieBaker.com?param=value");
request.withCredentials = true; // Tell the browser to receive cookies
request.send();
Is there anything else that could block the cookie from being stored in the browser? These are all my access Control headers included in the GET response (localhost is the "real" name of munchies.com for my testing):
access-control-allow-credentials: true
access-control-allow-headers: Authorization, Content-Type
access-control-allow-methods: OPTIONS, GET, POST, PUT, PATCH, DELETE
access-control-allow-origin: http://localhost
access-control-expose-headers: X-WP-Total, X-WP-TotalPages
You have explicitly set SameSite=Lax on the cookiebaker.com cookie, which will restrict it from being sent in a cross-site context, e.g. a fetch() originating from munchies.com.
For an explicit cross-site cookie, you should use SameSite=None;Secure. For more in-depth implementation detail, see https://web.dev/samesite-cookie-recipes

Cognito logout does not work as documented

I have a Cognito user pool configured with a SAML identity provider (ADFS) and I'm able to sign it as a federated user (AD) but sign out does not work.
Following the documentation, I make a GET request to
https://my-domain.auth.us-west-2.amazoncognito.com/logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com (using some public logout uri), from my client (an AngularJS 1.x app), and I get back a 302 with a Location header like
https://my-domain.auth.us-west-2.amazoncognito.com/login?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
(In fact there I see 2 requests like the above).
When I log back in (thru ADFS) it does not prompt for my AD credentials, i.e. seems that I'm not logged out.
My user pool is configured as described here (see step 7), where the Enable IdP sign out flow is checked, which is supposed to log the user out from ADFS as well.
Any suggestions?
Thanks.
General
-------
Request URL: https://my-domain.auth.us-west-2.amazoncognito.com/logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
Request Method: GET
Status Code: 302
Remote Address: 54.69.30.36:443
Referrer Policy: no-referrer-when-downgrade
Response Headers
----------------
cache-control: private
content-length: 0
date: Fri, 20 Apr 2018 21:31:12 GMT
expires: Thu, 01 Jan 1970 00:00:00 UTC
location: https://my-domain.auth.us-west-2.amazoncognito.com/login?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
server: Server
set-cookie: XSRF-TOKEN=...; Path=/; Secure; HttpOnly
set-cookie: XSRF-TOKEN=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/; Secure; HttpOnly
status: 302
strict-transport-security: max-age=31536000 ; includeSubDomains
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
Request Headers
---------------
:authority: my-domain.auth.us-west-2.amazoncognito.com
:method: GET
:path: /logout?client_id=63...ng&logout_uri=http:%2F%2Fyahoo.com
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9
authorization: Bearer eyJra...
cache-control: no-cache
origin: https://localhost:8443
pragma: no-cache
referer: https://localhost:8443/logout
user-agent: Mozilla/5.0...
This redirect happens whenever logout_uri parameter doesn't match exactly what's listed among Sign out URL(s) in AWS Cognito User Pools App client settings configuration.
Cognito allows logout with either logout_uri or with the same arguments as login (i.e. redirect_uri and response_type) to log out and take the user back to the login screen. It seems that whenever logout_uri is invalid, it assume the re-login flow, does this redirect, and then reports an error about missing login arguments.
As for SAML, I don't know, but guessing that it doesn't work because there was actually an error, just not properly reported.
The /logout endpoint signs the user out.It only supports HTTPS GET. It is working
Sample Requests - Logout and Redirect Back to Client
It clears out the existing session and redirects back to the client. Both parameters are required.
GET https://<YOUR DOMAIN NAME>/logout?
client_id=xxxxxxxxxxxx&
logout_uri=com.myclientapp://myclient/logout
Also make sure that Logout URL is same as SIGNOUT URL in AWS Cognito APP too.
for more information, refer AWS LOGOUT Endpoint
Finally I was able to fix this issue. I found the actual cause of the issue from the event viewer of my windows server 2012 R2. It says the following details about the failed sign out flow.
The SAML Single Logout request does not correspond to the logged-in session participant.
Requestor: urn:amazon:cognito:sp:userpoolid
Request name identifier: Format: urn:oasis:names:tc:SAML:2.0:nameid-format:persistent, NameQualifier: SPNameQualifier: , SPProvidedId:
Logged-in session participants:
Count: 1, [Issuer: urn:amazon:cognito:sp:userpoolid, NameID: (Format: , NameQualifier: SPNameQualifier: , SPProvidedId: )]
User Action
Verify that the claim provider trust or the relying party trust configuration is up to date. If the name identifier in the request is different from the name identifier in the session only by NameQualifier or SPNameQualifier, check and correct the name identifier policy issuance rule using the AD FS 2.0 Management snap-in.
The Error clearly says that the name identifier in the request is different from the name identifier in the session only by NameQualifier. I have corrected this error in the claim issuance tab of relying party trusts by adding the rule as below. The below rule replace the user#myadfsdomain to simply user when issuing the claim.
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname"]
=> issue(Type = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Issuer = c.Issuer, OriginalIssuer = c.OriginalIssuer, Value = RegExReplace(c.Value, "(?i)^myadfsdomain*\\", ""), ValueType = c.ValueType, Properties["http://schemas.xmlsoap.org/ws/2005/05/identity/claimproperties/format"] = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");
Besides this i have forgot to check in the enable signout flow in the cognito configuration which caused the problem. Logout started working seamlessly for me.
From documentation here
https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html
If you want
And your login url is like
"https://xxxx.auth.eu-west-1.amazoncognito.com/login?client_id=1234&response_type=token&scope=aws.cognito.signin.user.admin+email+openid+phone+profile&redirect_uri=http://localhost:3000/"
Then your logout url is like
"https://xxxx.auth.eu-west-1.amazoncognito.com/logout?client_id=1234&logout_uri=http://localhost:3000/";
Note the difference
login > login? and redirect_uri
logout > logout? and logout_uri
Those redirect / logout uri must match what you have configured inside Cognito.
If you don't do it right, you may get strange errors like
error=unauthorized_client
or
Required String parameter 'response_type' is not present
and who knows what else. :o)
My solution below is based on -
https://github.com/nextauthjs/next-auth/discussions/3938#discussioncomment-2165155
I am able to solve the issue with few changes-
in signout button upon clicking i will call handleSignOut
function handleSignOut() {
const clientId = 'paste your AWS COGNITO CLIENT ID';
const domain = 'paste your AWS COGNITO DOMAIN';
window.location.href = `${domain}/logout?client_id=${clientId}&logout_uri=http://localhost:3000/logout`
}
In Aws cognito-> app integration -> app client settings -> signout url
keep the following url-
http://localhost:3000/logout
in pages create a seperate page called logout and keep following code-
import { useEffect } from "react"
import { signOut } from 'next-auth/react'
export default function SignoutPage() {
useEffect(() => {
setTimeout(() => {
signOut({ callbackUrl: window.location.origin });
}, 1000);
}, []);
return (
<>
<div className="loader">
Loading..
</div>
<style jsx>{`
.loader{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transform: -webkit-translate(-50%, -50%);
transform: -moz-translate(-50%, -50%);
transform: -ms-translate(-50%, -50%);
}
`}</style>
</>
)
}
click signout it will log you out
#RequestMapping(value = "/logout", method = RequestMethod.GET)
public void logout(HttpServletRequest request, HttpServletResponse response) {
LOGGER.info("Logout controller");
try {
Cookie awsCookie = new Cookie("AWSELBAuthSessionCookie-0", "deleted");
awsCookie.setMaxAge(-1);
awsCookie.setPath("/");
response.addCookie(awsCookie);
Properties appProperties = new Properties();
applicationPropertyConfigurer.loadProperties(appProperties);
response.sendRedirect(appProperties.getProperty("logout.url"));
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, max-age=0");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", -1);
request.getSession().invalidate();
} catch (IOException e) {
LOGGER.error("Exception in redirecting to logout.url URL", e);
}
}
//https:/domain_name.auth.us-west-x.amazoncognito.com/logout?response_type=code&client_id=&redirect_uri=redirect_uri_should_be_present_in_cognito_user_pool_Callback URL&state=STATE&scope=openid

why is XMLHttpRequest.withCredentials necessary even for same site Ajax requests

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

JMeter can`t Log in using cookies

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