GithubHelp home page GithubHelp logo

Comments (8)

kettanaito avatar kettanaito commented on August 24, 2024

Hi, @ernestostifano. Thanks for reporting this!

I am frankly a bit lost in our existing cookie logic. It's been some years since it was originally written, and I'm trying to dig for the context behind every decision. I suspect that most of the things we have right now can be thrown away in v2.0.

Also, @chrisguttandin has referenced an issue in the cookie store package that may be helpful to me in uncovering the context:

#435

I've thrown together a new implementation for the cookie store, the one that will respect path and also domain: mswjs/cookies#34. But I need to understand the logic we have in place now before making any changes to MSW.

In summary, there are two surfaces dealing with cookies:

  • Response cookies. Whenever you have Set-Cookie on a mocked response, MSW writes those cookies to document.cookie.
  • Request cookie forwarding. When you make a request A that sets mocked cookies in the response, and then request B with a matching origin/path, MSW should include the relevant cookies in the request B.
  • Response resolver cookies. This can be similar to no.2 but I suspect we gather these cookies just to present to the developer. Ideally, it should be the same as no.2.

from msw.

kettanaito avatar kettanaito commented on August 24, 2024

I've also discovered that JSDOM doesn't write cookies to document.cookie if their path doesn't match the mocked location address. This is incorrect and is not how the browser behaves, after which all JSDOM behaviors must be modeled.

In the browser, you can write any cookies to document.cookie. Then, only when making a request, given the permissive credentials option, the browser will see which cookies from document.cookie are relevant. You can provide this behavior by yourself by writing a cookie with a non-matching path and opening Application > Cookies in the devtools. You will see all document cookies.

In JSDOM, nothing will be written.

// jsdom
// Given location is the default location of
// http://localhost/
document.cookie = 'a=b; Path=/foo'

This makes automated testing on our end a bit problematic. We may consider switching the test suite of the cookie store to run in the actual browser. That library is still designed for Node.js as well to act as a singleton and a replacement layer for cookies in Node.js (where no document.cookie exists).

from msw.

kettanaito avatar kettanaito commented on August 24, 2024

Opened a branch with a WIP fix/wip-cookie-path-match.

from msw.

kettanaito avatar kettanaito commented on August 24, 2024

Released: v2.3.3 🎉

This has been released in v2.3.3!

Make sure to always update to the latest version (npm i msw@latest) to get the newest features and bug fixes.


Predictable release automation by @ossjs/release.

from msw.

ernestostifano avatar ernestostifano commented on August 24, 2024

Hi @kettanaito, I just had the chance to upgrade and test this out, but unfortunately the issue is still there (2.3.3 and 2.3.5). You can check the updated CodeSandbox.

I also noticed the following strange behaviour:

  • Both cookies are visible in the cookies storage, but in the local storage, within __msw-cookie-store__, only the first cookie is stored.

from msw.

ernestostifano avatar ernestostifano commented on August 24, 2024

@kettanaito If I switch the two cookies (putting the one with the non-root path first), the behavior changes.

A) badCookie is included in every API call:

    http.get("/set-cookies", () => {
      return HttpResponse.text("COOKIES SET", {
        headers: [
          [
            "Set-Cookie",
            "badCookie=badCookie; Path=/some/path; SameSite=None; Secure; Max-Age=20;",
          ],
          [
            "Set-Cookie",
            "goodCookie=goodCookie; Path=/; SameSite=None; Secure; Max-Age=5;",
          ],
        ],
      });
    })

B) badCookie is not included in any API call:

    http.get("/set-cookies", () => {
      return HttpResponse.text("COOKIES SET", {
        headers: [
          [
            "Set-Cookie",
            "goodCookie=goodCookie; Path=/; SameSite=None; Secure; Max-Age=5;",
          ],
          [
            "Set-Cookie",
            "badCookie=badCookie; Path=/some/path; SameSite=None; Secure; Max-Age=20;",
          ],
        ],
      });
    })

In both cases, max-age seems to get mixed between the two cookies. For example, with the B configuration above, in the CodeSandbox, try the following:

  1. Click Set Cookies.
  2. Wait a little over 5 seconds for goodCookie to expire (do not click anything in the meantime).
  3. Click on Get Cookies (/some). You will see no cookies as expected
  4. Click on Get Cookies (/some/path). You will see goodCookie as if it took badCookie's place. It will then expire when badCookie should have expired (~20s) (click on Get Cookies (/some/path) again).

EDIT

I get different results from different combinations of the above: different order of cookies, different order of operations (setting/using cookies). Even using a cookie (making an API call) before the cookie expires and then using it again produces different results than just trying to use the cookie after it expired.

I also think there are alignment issues between the cookies storage and the local storage. For example, what if I clear the cookies storage and not the local storage? Or if I delete a specific cookie manually from the inspector.

from msw.

ernestostifano avatar ernestostifano commented on August 24, 2024

@kettanaito here is a similar example, emulating a real-world use-case: CodeSandbox.

  1. Set the following cookies (Set Cookies button) (order matters, as per my previous comment):

    A)
    - name: session-token
    - value session-token-value
    - path: /session/refresh-token
    - max-age: 200

    B)
    - name: access-token
    - value access-token-value
    - path: /
    - max-age: 5

  2. At this point, all API calls include both access-token and session-token, disregarding the paths (Echo Cookies and Refresh Token buttons).

  3. Wait for the second cookie to expire.

  4. Make an API call to /session/refresh-token (Refresh Token button).

  5. One would expect only the session-token cookie to be included, but oddly access-token is included instead, and then it disappears too (I think sometimes it doesn't), after session-token expiration.

The behavior seems a bit broken. I think we should re-open this issue.

from msw.

kettanaito avatar kettanaito commented on August 24, 2024

@ernestostifano, thanks for diving deeper on this one!

I know you've done a lot already, but may I ask you to collaborate with me on turning these use cases you shown into integration tests for MSW? That would be the best way for the issue to move forward.

For example, what if I clear the cookies storage and not the local storage?

Afaik, there's no longer a concept of a cookie storage. Everything is stored either in-memory (for Node.js) or in local storage.

Or if I delete a specific cookie manually from the inspector.

Then I'd say you know what you are doing and should expect the behavior to change respectively.

I trust tough-cookie is an extremely well-tested package, so if there are any issues, they are likely in how MSW uses it. That's where having automated tests to prove that would be great.

Test references

Important

Also please note there are two separate features related to cookies that may overlap. First, there's how MSW exposes request cookies in the cookies argument of the response resolver. Second, is how MSW treats your mocked cookies from the Set-Cookie mocked response header. There are flows when both are involved, and flows where only one is (the request cookies).

from msw.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.