jeremydaly / jeremydaly/lambda-api

Watch out for setCookie and clearCookie opts, as clearCookie mutates opts instance

Open
#165 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
1.5k
Forks
127
Avg merge
31m
Merged PRs (30d)
2

Description

Hi!

Lambda-api is a great package and has been really helpful in our one single backend lambda architecture 🚀

Today, I faced a really hard to debug issue and I though passing the information here if it helps anyone else. I was running our app with SAM CLI and everything worked like a charm, but when I deployed to AWS lambda, it worked in really weird way.

The problem was that I had somewhat the following structure:

const cookieOptions: CookieOptions = {
  sameSite: 'Lax',
  secure: true,
  httpOnly: true,
}

// And pseudo authentication

api.post('/login', async (req,res) => {
  // Do login stuff
  res.setCookie('authcookie', sessionId, cookieOpts).json({ message: "Authenticated" })
})

api.post('/logout', async (req,res) => {
  // Do logout stuff
  res.clearCookie('authcookie', cookieOpts).json({ message: "Logged out" })
})

It worked locally like a charm. But when it was deployed to AWS Lambda, it worked like this:

  1. Login - OK you get a cookie
  2. Logout - OK cookie gets evicted
  3. Login - FAILS and no cookie is set
  4. After some time, it starts to work again, for once 😂

After some weird debugging I found out that the third call gets correct cookie, but with expiration set to -1, so the browser thinks that yes, unset the cookie.

Obvious reason for this is that response.clearCookie call mutates the given opts instance:

https://github.com/jeremydaly/lambda-api/blob/0b753d855fd5be1f25b5291c78f25171d2151e19/lib/response.js#L221-L224

After the first response.clearCookie call my opts instance was mutated to contain expiration: -1 field and calls to response.setCookie after that naturally included the expiration.

The reason why it works after some time in AWS lambda is that runtime spins up a new sandbox and then it works again, for once 😄, because opts instance is not mutated and no one has logged out. Locally SAM CLI fires a whole new sandbox for each request and thus it worked locally with no issues.

You can easily work around this for example by wrapping opts in function:

const cookieOptions = (): CookieOptions => ({
  sameSite: 'Lax',
  secure: true,
  httpOnly: true,
})

It surely took me some time to figure out what is going on 😄 I don't think this is mentioned in documentation. I think I can work out a PR for this if this is something that should be fixed - in code or with documentation.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lib/response.js at lines 221-224 and inspect how clearCookie handles the supplied options object. Reproduce the login, logout, and login sequence with shared cookie options, then verify that clearing a cookie does not affect later setCookie calls; document the behavior if the implementation is intentionally unchanged.

Written by the indexing model from the issue text.

Assessment

Tech stack
aws, javascript
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.