Passing additional data in tus.handle() to use in callback

I use uppy/tus and tus-node-server in my project. On the server I have fastify which accepts uploads on endpoints where I have CORS enabled and handle the requests appropriately. It sends them to my TusServer which is connected to my S3 storage. On these endpoints I end up checking for an auth cookie and decrypt the user session, which leaves me with the userId and email. This all works well.

I’m struggling to add this user information to my S3 storage. Here’s what my code looks like currently:

  • Frontend (React)
 const [Uppy] = useState(() => {
    const uppyInstance = new Uppy({
      restrictions: {
        maxNumberOfFiles: 1,
        allowedFileTypes: ['.png', '.jpg', '.jpeg', '.gif'],
        maxFileSize: 10 * 1024 * 1024,
      },
      onBeforeUpload(files) {
        for (const [key, file] of Object.entries(files)) {
          uppyInstance.setFileMeta(key, {
            name: file.name,
            type: file.type,
            contentType: file.type,
          })
        }
        return true
      },
    }).use(Tus, {
      endpoint: endpointUrl,
      withCredentials: useCredentials,
    })
    return uppyInstance.on('complete', (result) => {
      setAvatar(result.successful[0]?.uploadURL || '')
    })
  })
  • Server (Node)
  server.all('/public', (req, res) => {
      if (req.method === 'OPTIONS') {
        handleOptionsRequest(res)
      } else if (['GET'].includes(req.method)) {
        tusS3.handle(req.raw, res.raw) // I let anyone see the images, this is fine
      } else if (['POST', 'HEAD', 'PATCH'].includes(req.method)) {
        if (req.headers.cookie) {
          handleAuthenticatedRequest(req, res, tusS3)
        } else {
          res.raw.statusCode = 401
          res.raw.end()
        }
      }
  })

function handleAuthenticatedRequest(req, res, tusHandler) {
  try {
    const sessionCookie = extractSessionCookie(req.headers.cookie)
    validateSessionCookie(sessionCookie)
    const [userId, email] = decryptAndValidateSession(sessionCookie)
    if (userId) {
      res.raw.setHeader('Access-Control-Allow-Credentials', 'true')
      // How do I pass userId and email to the handle to use it in the onUploadFinish callback?
      tusHandler.handle(req.raw, res.raw)
    } else {
      res.raw.statusCode = 403
      res.raw.end()
    }
  } catch (error) {
    res.raw.statusCode = 403
    res.raw.end()
  }
}

const tusS3 = new TusServer({
  path: '/public',
  datastore: s3storePublic,
  onResponseError: (error, req, res) => {
    logger.error(res)
  },
  respectForwardedHeaders: true,
  onUploadFinish: async (
    req: IncomingMessage,
    res: ServerResponse,
    upload: Upload
  ) => {
    const metadata = {
      name: upload.metadata.name, // This works just fine but how do I add the userId and e-mail here? I have it in the authenticated function above
    }
    const key = upload.id

    try {
      await addMetadataToS3Object('public', key, metadata, upload.metadata.type)
      return { res, status_code: 200 }
    } catch (error) {
      return { res, status_code: 500, body: 'Failed to add metadata' }
    }
  },
})

Yes I know I could add the userId and email on the frontend to the Uppy metadata, however this is not secure. I want to handle this server-side with the decrypted and validated cookie.

How do I pass the userId and email to the TusServer’s OnUploadFinish callback?

I think I fixed it by adding the information to the raw request:

req.raw.userId = userId
req.raw.userId = email

And then in the tus3 server in the onUploadFinish callback:

    const userId = (req as any).userId
    const email = (req as any).email

Now I can construct the metadata object to add it to the S3 storage.