koajs / koa

Expressive middleware for node.js using ES2017 async functions
https://koajs.com
MIT License
35.24k stars 3.23k forks source link

[fix] ctx value persists to next request after mutating ctx object #1795

Closed BenMaGit closed 10 months ago

BenMaGit commented 10 months ago

Describe the bug

Node.js version: 18

Description:

When mutating the ctx object within a middleware, the changes made to the ctx should be limited to the current request and should not persist to the next request. However, it appears that there is an issue where the mutated values in ctx persist to the next request, causing unintended side effects.

I know I should probably use Koa state for this kind of info, but still wondering if the is a expected behavior ?

Actual behavior

Changes made to the ctx object within a middleware persist to the next request, leading to unexpected behavior and potential bugs in applications.

Expected behavior

Mutations to the ctx object should be isolated to the current request and should not affect subsequent requests.

Code to reproduce

To replicate the issue, follow these steps by sending two consecutive requests to different API routes:

Initiate the first request to trigger the authenticate function, resulting in the mutation of the ctx object with an appended field. Subsequently, send the second request that should bypass the logic in the authentication function. Upon examination, you will notice that both request logs display the same userId, despite the second request being expected to have nothing

  app.use(logRequest()); -> Will log request metadata after await next()
  app.use(handleError());
  app.use(authenticate()); -> Will mutate the ctx object and append a userId field to it if the path is not in the whitelist
  app.use(myFirstRoute.routes());
  app.use(mySecondRoute.routes());

Checklist

siakc commented 10 months ago

You may send the code for authenticate() at least. I guess somehow your code is retaining values.

BenMaGit commented 10 months ago

I am mutating the ctx object directly in that function like this, nothing special. All the other default fields (host, method, path, headers, ip) all have the correct value (request data for the 2nd request), only the appended object persists from the last request, which won't be a problem if the consecutive request is another request that needs to be authenticated first, which will replace the user value with the token that comes with the request

const user = await getUserById(id)
ctx.user = user
BenMaGit commented 10 months ago

nvm, I think I found the culprit, ill close the issue for now.