Skip to content

Authentication Issue: Missing Cookie on Cross-Site Redirect by Button with Overseerr Assistant for Firefox #56

Description

@ZackEndboss

I encountered an issue with the Overseerr Assistant Firefox extension (v1.7.1) when accessing Overseerr (v1.33.2, running in a Docker container behind Traefik) via a cross-site redirect. Specifically, pressing the "Available" button on TheMovieDB redirects to Overseerr but lands on the login page instead of the intended movie page. However, manually entering the same URL directly into the browser works as expected.

This suggests that the session cookie (connect.sid) is not being transmitted during the cross-site redirect, potentially due to cookie handling or SameSite settings. Below, I’ve provided detailed steps to reproduce the issue, along with relevant debug logs and configuration details.

Steps to Reproduce:

  • Open Firefox (v133.0.3).
  • Navigate to TheMovieDB movie page.
  • Press the "Available" button to redirect to Overseerr.
  • Notice that you land on the Overseerr login page instead of the intended movie page.

Debug Information:

  • OS: Windows 11
  • Browser: Firefox 133.0.3
  • Extension: Overseerr Assistant 1.7.1
  • Overseerr Version: 1.33.2
  • Reverse Proxy: Traefik v3.0.4
  • Overseerr Reverse Proxy setting activated and restarted

Observations:

  • Directly entering the URL https://overseerr.domain.org/movie/429 in the browser works fine and shows the movie page.
  • Redirect from TheMovieDB results in a 307 response, redirecting to /login and directly to /.
  • The request headers during the redirect differ: the connect.sid cookie is missing, and the Sec-Fetch-Site is marked as cross-site.
  • Overseerr logs show a 401 Unauthorized error due to the missing session cookie.
overseerr log
overseerr  | AxiosError: Request failed with status code 401
overseerr  |     at settle (file:///app/node_modules/axios/lib/core/settle.js:19:12)
overseerr  |     at IncomingMessage.handleStreamEnd (file:///app/node_modules/axios/lib/adapters/http.js:556:11)
overseerr  |     at IncomingMessage.emit (node:events:525:35)
overseerr  |     at endReadableNT (node:internal/streams/readable:1358:12)
overseerr  |     at processTicksAndRejections (node:internal/process/task_queues:83:21) {
overseerr  |   code: 'ERR_BAD_REQUEST',
overseerr  |   config: {
overseerr  |     transitional: {
overseerr  |       silentJSONParsing: true,
overseerr  |       forcedJSONParsing: true,
overseerr  |       clarifyTimeoutError: false
overseerr  |     },
overseerr  |     adapter: [ 'xhr', 'http' ],
overseerr  |     transformRequest: [ [Function: transformRequest] ],
overseerr  |     transformResponse: [ [Function: transformResponse] ],
overseerr  |     timeout: 0,
overseerr  |     xsrfCookieName: 'XSRF-TOKEN',
overseerr  |     xsrfHeaderName: 'X-XSRF-TOKEN',
overseerr  |     maxContentLength: -1,
overseerr  |     maxBodyLength: -1,
overseerr  |     env: { FormData: [Function], Blob: null },
overseerr  |     validateStatus: [Function: validateStatus],
overseerr  |     headers: AxiosHeaders {
overseerr  |       Accept: 'application/json, text/plain, */*',
overseerr  |       'User-Agent': 'axios/1.3.4',
overseerr  |       'Accept-Encoding': 'gzip, compress, deflate, br'
overseerr  |     },
overseerr  |     method: 'get',
overseerr  |     url: 'http://localhost:5055/api/v1/movie/429',
overseerr  |     data: undefined
overseerr  |   },
overseerr  |   request: <ref *1> ClientRequest {
overseerr  |     _events: [Object: null prototype] {
overseerr  |       abort: [Function (anonymous)],
overseerr  |       aborted: [Function (anonymous)],
overseerr  |       connect: [Function (anonymous)],
overseerr  |       error: [Function (anonymous)],
overseerr  |       socket: [Function (anonymous)],
overseerr  |       timeout: [Function (anonymous)],
overseerr  |       prefinish: [Function: requestOnPrefinish]
overseerr  |     },
overseerr  |     _eventsCount: 7,
overseerr  |     _maxListeners: undefined,
overseerr  |     outputData: [],
overseerr  |     outputSize: 0,
overseerr  |     writable: true,
overseerr  |     destroyed: false,
overseerr  |     _last: true,
overseerr  |     chunkedEncoding: false,
overseerr  |     shouldKeepAlive: false,
overseerr  |     maxRequestsOnConnectionReached: false,
overseerr  |     _defaultKeepAlive: true,
overseerr  |     useChunkedEncodingByDefault: false,
overseerr  |     sendDate: false,
overseerr  |     _removedConnection: false,
overseerr  |     _removedContLen: false,
overseerr  |     _removedTE: false,
overseerr  |     _contentLength: 0,
overseerr  |     _hasBody: true,
overseerr  |     _trailer: '',
overseerr  |     finished: true,
overseerr  |     _headerSent: true,
overseerr  |     _closed: false,
overseerr  |     socket: Socket {
overseerr  |       connecting: false,
overseerr  |       _hadError: false,
overseerr  |       _parent: null,
overseerr  |       _host: 'localhost',
overseerr  |       _readableState: [ReadableState],
overseerr  |       _events: [Object: null prototype],
overseerr  |       _eventsCount: 7,
overseerr  |       _maxListeners: undefined,
overseerr  |       _writableState: [WritableState],
overseerr  |       allowHalfOpen: false,
overseerr  |       _sockname: null,
overseerr  |       _pendingData: null,
overseerr  |       _pendingEncoding: '',
overseerr  |       server: null,
overseerr  |       _server: null,
overseerr  |       parser: null,
overseerr  |       _httpMessage: [Circular *1],
overseerr  |       [Symbol(async_id_symbol)]: 101713,
overseerr  |       [Symbol(kHandle)]: [TCP],
overseerr  |       [Symbol(lastWriteQueueSize)]: 0,
overseerr  |       [Symbol(timeout)]: null,
overseerr  |       [Symbol(kBuffer)]: null,
overseerr  |       [Symbol(kBufferCb)]: null,
overseerr  |       [Symbol(kBufferGen)]: null,
overseerr  |       [Symbol(kCapture)]: false,
overseerr  |       [Symbol(kSetNoDelay)]: false,
overseerr  |       [Symbol(kSetKeepAlive)]: true,
overseerr  |       [Symbol(kSetKeepAliveInitialDelay)]: 60,
overseerr  |       [Symbol(kBytesRead)]: 0,
overseerr  |       [Symbol(kBytesWritten)]: 0,
overseerr  |       [Symbol(RequestTimeout)]: undefined
overseerr  |     },
overseerr  |     _header: 'GET /api/v1/movie/429 HTTP/1.1\r\n' +
overseerr  |       'Accept: application/json, text/plain, */*\r\n' +
overseerr  |       'User-Agent: axios/1.3.4\r\n' +
overseerr  |       'Accept-Encoding: gzip, compress, deflate, br\r\n' +
overseerr  |       'Host: localhost:5055\r\n' +
overseerr  |       'Connection: close\r\n' +
overseerr  |       '\r\n',
overseerr  |     _keepAliveTimeout: 0,
overseerr  |     _onPendingData: [Function: nop],
overseerr  |     agent: Agent {
overseerr  |       _events: [Object: null prototype],
overseerr  |       _eventsCount: 2,
overseerr  |       _maxListeners: undefined,
overseerr  |       defaultPort: 80,
overseerr  |       protocol: 'http:',
overseerr  |       options: [Object: null prototype],
overseerr  |       requests: [Object: null prototype] {},
overseerr  |       sockets: [Object: null prototype],
overseerr  |       freeSockets: [Object: null prototype] {},
overseerr  |       keepAliveMsecs: 1000,
overseerr  |       keepAlive: false,
overseerr  |       maxSockets: Infinity,
overseerr  |       maxFreeSockets: 256,
overseerr  |       scheduling: 'lifo',
overseerr  |       maxTotalSockets: Infinity,
overseerr  |       totalSocketCount: 1,
overseerr  |       [Symbol(kCapture)]: false
overseerr  |     },
overseerr  |     socketPath: undefined,
overseerr  |     method: 'GET',
overseerr  |     maxHeaderSize: undefined,
overseerr  |     insecureHTTPParser: undefined,
overseerr  |     path: '/api/v1/movie/429',
overseerr  |     _ended: true,
overseerr  |     res: IncomingMessage {
overseerr  |       _readableState: [ReadableState],
overseerr  |       _events: [Object: null prototype],
overseerr  |       _eventsCount: 4,
overseerr  |       _maxListeners: undefined,
overseerr  |       socket: [Socket],
overseerr  |       httpVersionMajor: 1,
overseerr  |       httpVersionMinor: 1,
overseerr  |       httpVersion: '1.1',
overseerr  |       complete: true,
overseerr  |       rawHeaders: [Array],
overseerr  |       rawTrailers: [],
overseerr  |       aborted: false,
overseerr  |       upgrade: false,
overseerr  |       url: '',
overseerr  |       method: null,
overseerr  |       statusCode: 401,
overseerr  |       statusMessage: 'Unauthorized',
overseerr  |       client: [Socket],
overseerr  |       _consuming: false,
overseerr  |       _dumped: false,
overseerr  |       req: [Circular *1],
overseerr  |       responseUrl: 'http://localhost:5055/api/v1/movie/429',
overseerr  |       redirects: [],
overseerr  |       [Symbol(kCapture)]: false,
overseerr  |       [Symbol(kHeaders)]: [Object],
overseerr  |       [Symbol(kHeadersCount)]: 12,
overseerr  |       [Symbol(kTrailers)]: null,
overseerr  |       [Symbol(kTrailersCount)]: 0,
overseerr  |       [Symbol(RequestTimeout)]: undefined
overseerr  |     },
overseerr  |     aborted: false,
overseerr  |     timeoutCb: null,
overseerr  |     upgradeOrConnect: false,
overseerr  |     parser: null,
overseerr  |     maxHeadersCount: null,
overseerr  |     reusedSocket: false,
overseerr  |     host: 'localhost',
overseerr  |     protocol: 'http:',
overseerr  |     _redirectable: Writable {
overseerr  |       _writableState: [WritableState],
overseerr  |       _events: [Object: null prototype],
overseerr  |       _eventsCount: 3,
overseerr  |       _maxListeners: undefined,
overseerr  |       _options: [Object],
overseerr  |       _ended: true,
overseerr  |       _ending: true,
overseerr  |       _redirectCount: 0,
overseerr  |       _redirects: [],
overseerr  |       _requestBodyLength: 0,
overseerr  |       _requestBodyBuffers: [],
overseerr  |       _onNativeResponse: [Function (anonymous)],
overseerr  |       _currentRequest: [Circular *1],
overseerr  |       _currentUrl: 'http://localhost:5055/api/v1/movie/429',
overseerr  |       [Symbol(kCapture)]: false
overseerr  |     },
overseerr  |     [Symbol(kCapture)]: false,
overseerr  |     [Symbol(kNeedDrain)]: false,
overseerr  |     [Symbol(corked)]: 0,
overseerr  |     [Symbol(kOutHeaders)]: [Object: null prototype] {
overseerr  |       accept: [Array],
overseerr  |       'user-agent': [Array],
overseerr  |       'accept-encoding': [Array],
overseerr  |       host: [Array]
overseerr  |     },
overseerr  |     [Symbol(kUniqueHeaders)]: null
overseerr  |   },
overseerr  |   response: {
overseerr  |     status: 401,
overseerr  |     statusText: 'Unauthorized',
overseerr  |     headers: AxiosHeaders {
overseerr  |       'x-powered-by': 'Express',
overseerr  |       'content-type': 'application/json; charset=utf-8',
overseerr  |       'content-length': '131',
overseerr  |       etag: 'W/"83-xxxxxxxxxxxxxxxxxxxxxx"',
overseerr  |       date: 'Sat, 04 Jan 2025 16:28:06 GMT',
overseerr  |       connection: 'close'
overseerr  |     },
overseerr  |     config: {
overseerr  |       transitional: [Object],
overseerr  |       adapter: [Array],
overseerr  |       transformRequest: [Array],
overseerr  |       transformResponse: [Array],
overseerr  |       timeout: 0,
overseerr  |       xsrfCookieName: 'XSRF-TOKEN',
overseerr  |       xsrfHeaderName: 'X-XSRF-TOKEN',
overseerr  |       maxContentLength: -1,
overseerr  |       maxBodyLength: -1,
overseerr  |       env: [Object],
overseerr  |       validateStatus: [Function: validateStatus],
overseerr  |       headers: [AxiosHeaders],
overseerr  |       method: 'get',
overseerr  |       url: 'http://localhost:5055/api/v1/movie/429',
overseerr  |       data: undefined
overseerr  |     },
overseerr  |     request: <ref *1> ClientRequest {
overseerr  |       _events: [Object: null prototype],
overseerr  |       _eventsCount: 7,
overseerr  |       _maxListeners: undefined,
overseerr  |       outputData: [],
overseerr  |       outputSize: 0,
overseerr  |       writable: true,
overseerr  |       destroyed: false,
overseerr  |       _last: true,
overseerr  |       chunkedEncoding: false,
overseerr  |       shouldKeepAlive: false,
overseerr  |       maxRequestsOnConnectionReached: false,
overseerr  |       _defaultKeepAlive: true,
overseerr  |       useChunkedEncodingByDefault: false,
overseerr  |       sendDate: false,
overseerr  |       _removedConnection: false,
overseerr  |       _removedContLen: false,
overseerr  |       _removedTE: false,
overseerr  |       _contentLength: 0,
overseerr  |       _hasBody: true,
overseerr  |       _trailer: '',
overseerr  |       finished: true,
overseerr  |       _headerSent: true,
overseerr  |       _closed: false,
overseerr  |       socket: [Socket],
overseerr  |       _header: 'GET /api/v1/movie/429 HTTP/1.1\r\n' +
overseerr  |         'Accept: application/json, text/plain, */*\r\n' +
overseerr  |         'User-Agent: axios/1.3.4\r\n' +
overseerr  |         'Accept-Encoding: gzip, compress, deflate, br\r\n' +
overseerr  |         'Host: localhost:5055\r\n' +
overseerr  |         'Connection: close\r\n' +
overseerr  |         '\r\n',
overseerr  |       _keepAliveTimeout: 0,
overseerr  |       _onPendingData: [Function: nop],
overseerr  |       agent: [Agent],
overseerr  |       socketPath: undefined,
overseerr  |       method: 'GET',
overseerr  |       maxHeaderSize: undefined,
overseerr  |       insecureHTTPParser: undefined,
overseerr  |       path: '/api/v1/movie/429',
overseerr  |       _ended: true,
overseerr  |       res: [IncomingMessage],
overseerr  |       aborted: false,
overseerr  |       timeoutCb: null,
overseerr  |       upgradeOrConnect: false,
overseerr  |       parser: null,
overseerr  |       maxHeadersCount: null,
overseerr  |       reusedSocket: false,
overseerr  |       host: 'localhost',
overseerr  |       protocol: 'http:',
overseerr  |       _redirectable: [Writable],
overseerr  |       [Symbol(kCapture)]: false,
overseerr  |       [Symbol(kNeedDrain)]: false,
overseerr  |       [Symbol(corked)]: 0,
overseerr  |       [Symbol(kOutHeaders)]: [Object: null prototype],
overseerr  |       [Symbol(kUniqueHeaders)]: null
overseerr  |     },
overseerr  |     data: { message: "cookie 'connect.sid' required", errors: [Array] }
overseerr  |   }
overseerr  | }
overseerr  | Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
overseerr  |     at new NodeError (node:internal/errors:387:5)
overseerr  |     at ServerResponse.setHeader (node:_http_outgoing:603:11)
overseerr  |     at ServerResponse._res.setHeader (/app/node_modules/next/dist/server/base-server.js:127:24)
overseerr  |     at setHeadersFromObject (/app/node_modules/next/dist/compiled/compression/index.js:46:680)
overseerr  |     at ServerResponse.setWriteHeadHeaders (/app/node_modules/next/dist/compiled/compression/index.js:46:914)
overseerr  |     at ServerResponse.writeHead (/app/node_modules/next/dist/compiled/compression/index.js:46:121)
overseerr  |     at Function.CoreApp.getInitialProps (/app/.next/server/pages/_app.js:18211:19)
overseerr  |     at runMicrotasks (<anonymous>)
overseerr  |     at processTicksAndRejections (node:internal/process/task_queues:96:5) {
overseerr  |   code: 'ERR_HTTP_HEADERS_SENT'
overseerr  | }

docker-compose.yml
services:
  overseerr:
    image: sctx/overseerr:latest
    container_name: overseerr
    environment:
      TZ: "Europe/Berlin"
    expose:
      - 5055
    volumes:
      - "${CONFIG_PATH}/overseerr/config:/app/config"
    restart: unless-stopped
    labels:
      # Reverse Proxy
      - "traefik.enable=true"
      - "traefik.http.routers.overseerr.rule=Host(`overseerr.${DOMAIN}`)"
      - "traefik.http.routers.overseerr.entrypoints=websecure"
      - "traefik.http.routers.overseerr.tls.certresolver=infra"
      - "traefik.http.routers.overseerr.service=overseerr"
      - "traefik.http.services.overseerr.loadbalancer.server.port=5055"
      # Healthcheck
      - "traefik.http.services.overseerr.loadbalancer.healthcheck.interval=600s"
      - "traefik.http.services.overseerr.loadbalancer.healthcheck.timeout=3s"
      # Watchtower
      - "com.centurylinklabs.watchtower.enable=true"
    networks:
      - dlinfra
...

networks:
  dlinfra:
    external: true
plugin-settings

Overseerr Assistant FF Plugin Settings

Additional Context:

I’m not entirely sure if this is a plugin issue or a result of specific settings in Overseerr, Traefik, or my environment. However, this behavior makes the extension unusable in its current state. I’d appreciate any guidance or suggestions for resolving this issue.

Let me know if you’d like further details or additional logs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions