-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(multi-domain): Handle SameSite cookies #20450
fix(multi-domain): Handle SameSite cookies #20450
Conversation
data sent through the communicator has to be an object, or else 'a string' gets turned into { 0: 'a', 1: ' ', 2: 's', ... }
Thanks for taking the time to open a PR!
|
Test summaryRun details
View run in Cypress Dashboard ➡️ Flakiness
This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard |
packages/driver/cypress/integration/e2e/multi-domain/cookie_login_spec.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a temporary solution for the experimental release of multi-domain support. In the future, this will be replaced with a more robust solution that provides the necessary cookies to cross-origin requests without changing the SameSite attribute of the cookie when a request attempts to set it.
@chrisbreiding this is the "don't modify the broken header, just use automation to force it to be set" approach, correct? Is there already a task created for this somewhere?
}) | ||
}) | ||
|
||
describe('cookie modification specifics - Firefox + https://localhost', function () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: kinda feels like all these should be nested describe
s in a top-level describe('cookie modification specifics')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in c0a3ce8
const browser = this.getCurrentBrowser() || { family: null } | ||
const url = new URL(this.req.proxiedUrl) | ||
const isLocalhost = uri.isLocalhost(url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these 3 lines should go inside of if (cookies)
, since they are not used outside of that scope
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in e88519a
@@ -43,7 +44,10 @@ type HttpMiddlewareCtx<T> = { | |||
debug: Debug.Debugger | |||
middleware: HttpMiddlewareStacks | |||
deferSourceMapRewrite: (opts: { js: string, url: string }) => string | |||
getCurrentBrowser: () => Browser | Partial<Browser> & Pick<Browser, 'family'> | null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my apologies for all the changes you had to make to this file... in retrospect, this was an unmaintainable way to set up how ctx
works. this area needs a refactor, like, yesterday. not related to this PR, just sharing my shame 😄
|
||
const localhostIPRegex = /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ | ||
|
||
export function isLocalhost (url: URL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about localhost6
? 😛 i'm mostly kidding... but i do think that this area might be error-prone, especially since i don't see any links to RFCs or browser source code showing how they do this heuristic. do you have any references for how browsers decide this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in a2f7bd8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is similar code in https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cypress/location.ts#L16 for verifying localhost as well. Seems like it would be pretty simple to update that code to use this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that regex you linked actually looks wrong, i don't think 0.0.0.0
resolves to localhost
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why it's specifically checking for localhost there. Seems like it should just be a general IP address check since it's trying to determine if it's a url it can normalize.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe it's addressing this requirement:
Cypress automatically prepends the http:// protocol to common hosts. If you're not using one of these 3 hosts, then make sure to provide the protocol.
cy.visit('localhost:3000') // Visits http://localhost:3000
cy.visit('0.0.0.0:3000') // Visits http://0.0.0.0:3000
cy.visit('127.0.0.1:3000') // Visits http://127.0.0.1:3000
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah good find. In that case, it seems like that variable name is a bit misleading, but we probably shouldn't change the behavior since it's documented that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except for maybe 0.0.0.0
... I don't think there's any possible way to load something from the null route
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like there are some references to using 0.0.0.0
in the issues: https://github.com/cypress-io/cypress/issues?q=is%3Aissue+0.0.0.0+
private sendDebuggerCommandFn: SendDebuggerCommand, | ||
onFn: OnFn, | ||
private automation: Automation, | ||
private options: any, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you type this please, and maybe move sendDebuggerCommandFn
, onFn
, and automation
to this options
object? let's keep this signature as clean as possible, it has been ballooning
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in 4ed06ad
// Secure is required in Chrome for SameSite=None cookies to be set, but if | ||
// in Firefox and http://localhost, it causes the cookie not to be set, | ||
// so we need to ensure there is no Secure in that case |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd also like to see some links to defintiive documentation (RFC, source code, authoritative reference...) for this, i believe you, but it's important to clearly track browser errata like this since it changes under our feet all the time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dug a bit deeper into this and the reason there's a difference in Firefox is that it doesn't consider http://localhost
to be a secure context when loaded by the Cypress-launched browser, even though it's normally considered a secure context.
Here's an existing issue about it. It has something to do with the way we launch Firefox. I dug into it a little bit, but it needs a bit more research and I decided to time-box the effort since it's out of scope for this work.
The fact is that http://localhost
won't allow SameSite=None; Secure
cookies to be set even outside of multi-domain. The question then is whether we should have this workaround at all to support it for multi-domain when it doesn't even work in general. I decided to keep the workaround in for the following reasons:
- It's only going in the experimental release and will be removed once better cookie handling is implemented.
- The primary use-case for multi-domain is authentication, so I think it's important to get cookies working consistently between browsers for authentication purposes.
- We may need a similar workaround outside of multi-domain anyway, though I don't want to go that far at this moment. I feel it's best to scope this to the multi-domain work and experimental release instead of doing a general workaround without doing more research into why Firefox doesn't consider the browser a secure context.
I improved the comment here, explaining what I did above in brief and linking to the spec regarding secure contexts.
Yeah, that's right. Here's the issue for it. |
User facing changelog
N/A
Additional details
For multi-domain authentication workflows, when the secondary domain attempts to set a cookie and the cookie is
SameSite=Strict/Lax
, the browser won't set it sincetop
is a different origin. This is also the case when navigating back to the primary domain, even though the origin is the same astop
, since it's cross-origin and not a top-level navigation.To resolve this, when a request attempts to set a cookie for a request from the AUT that is cross-origin, we force the cookie to be
SameSite=None; Secure
, so that it gets set by the browser. Firefox won't setSameSite=None; Secure
cookies forhttp://localhost
in an iframe, so we remove theSecure
flag in that case.This is a temporary solution for the experimental release of multi-domain support. In the future, this will be replaced with a more robust solution that provides the necessary cookies to cross-origin requests without changing the SameSite attribute of the cookie when a request attempts to set it.
PR Tasks
cypress-documentation
?type definitions
?cypress.schema.json
?