Skip to content
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

Merged
merged 32 commits into from
Mar 22, 2022

Conversation

chrisbreiding
Copy link
Contributor

@chrisbreiding chrisbreiding commented Mar 2, 2022

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 since top is a different origin. This is also the case when navigating back to the primary domain, even though the origin is the same as top, 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 set SameSite=None; Secure cookies for http://localhost in an iframe, so we remove the Secure 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

  • Have tests been added/updated?
  • N/A Has the original issue (or this PR, if no issue exists) been tagged with a release in ZenHub? (user-facing changes only)
  • N/A Has a PR for user-facing changes been opened in cypress-documentation?
  • N/A Have API changes been updated in the type definitions?
  • N/A Have new configuration options been added to the cypress.schema.json?

@chrisbreiding chrisbreiding requested a review from a team as a code owner March 2, 2022 18:46
@chrisbreiding chrisbreiding requested review from jennifer-shehane and removed request for a team March 2, 2022 18:46
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Mar 2, 2022

Thanks for taking the time to open a PR!

@chrisbreiding chrisbreiding marked this pull request as draft March 2, 2022 19:11
@chrisbreiding chrisbreiding removed the request for review from jennifer-shehane March 2, 2022 19:11
@cypress
Copy link

cypress bot commented Mar 4, 2022



Test summary

5273 0 71 1Flakiness 3


Run details

Project cypress
Status Passed
Commit 796df1d
Started Mar 21, 2022 6:25 PM
Ended Mar 21, 2022 6:40 PM
Duration 14:50 💡
OS Linux Debian - 10.10
Browser Electron 94

View run in Cypress Dashboard ➡️


Flakiness

commands/actions/click_spec.js Flakiness
1 ... > throws when any member of the subject isnt visible
settings_spec.js Flakiness
1 Settings > file preference panel > loads preferred editor, available editors and shows spinner
cy/timers_spec.js Flakiness
1 driver/src/cy/timers > can cancel setIntervals paused or unpaused

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

Copy link
Contributor

@flotwig flotwig left a 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 () {
Copy link
Contributor

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 describes in a top-level describe('cookie modification specifics')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in c0a3ce8

Comment on lines 440 to 442
const browser = this.getCurrentBrowser() || { family: null }
const url = new URL(this.req.proxiedUrl)
const isLocalhost = uri.isLocalhost(url)
Copy link
Contributor

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

Copy link
Contributor Author

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
Copy link
Contributor

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a2f7bd8

Copy link
Contributor

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.

Copy link
Contributor

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

Copy link
Contributor Author

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.

Copy link
Contributor

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

https://docs.cypress.io/api/commands/visit#Protocol

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor

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,
Copy link
Contributor

@flotwig flotwig Mar 17, 2022

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 4ed06ad

Comment on lines 419 to 421
// 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
Copy link
Contributor

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...

Copy link
Contributor Author

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.

@chrisbreiding
Copy link
Contributor Author

@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?

Yeah, that's right. Here's the issue for it.

@AtofStryker AtofStryker self-requested a review March 21, 2022 19:08
@chrisbreiding chrisbreiding merged commit 22b35be into feature-multidomain Mar 22, 2022
@chrisbreiding chrisbreiding deleted the issue-19849-auth-workflow branch March 22, 2022 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants