How can I do SSE and auth session at the same time without a 302 redirect?

Topic summary

A developer is implementing a Server-Sent Events (SSE) progress bar but encounters authentication conflicts. When attempting to retrieve the session ID using authenticate.admin(request), the authentication middleware returns a 302 redirect to the login page, which breaks the SSE connection.

Technical Issue:

  • The EventSource receives a “text/html” MIME type instead of the required “text/event-stream”
  • The 302 redirect from authentication prevents the SSE stream from establishing

Current Implementation:

  • Uses authenticate.admin(request) to get session ID
  • Needs session ID to query database for client progress
  • SSE setup includes event stream with interval-based updates

Key Questions:

  • How to handle authentication without triggering a redirect that breaks SSE?
  • Is there an alternative method to pass the session ID to the SSE loader?

The discussion remains open with no resolution provided yet. The code snippets show both the authentication attempt and SSE implementation structure.

Summarized with AI on November 8. AI used: claude-sonnet-4-5-20250929.

I’m trying to make a progress bar with SSE. But I need to get the session id and then call the DB to get the clients progress to pass back.

But when trying to get the session id

const { session } = await authenticate.admin(request)

This error occurs

EventSource's response has a MIME type ("text/html") that is not "text/event-stream". Aborting the connection.

Because the authenticate line is returning a 302, wanting to redirect the client to login. This breaks the SSE.

This is the catched response from authenticate

Response {
    [Symbol(realm)]: { settingsObject: {} },
    [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: false,
    requestIncludesCredentials: false,
    type: 'default',
    status: 302,
    timingInfo: null,
    cacheState: '',
    statusText: '',
    headersList: HeadersList {
        cookies: null,
        [Symbol(headers map)]: [Map],
        [Symbol(headers map sorted)]: null
    },
    urlList: []
    },
    [Symbol(headers)]: HeadersList {
    cookies: null,
    [Symbol(headers map)]: Map(1) { 'location' => [Object] },
    [Symbol(headers map sorted)]: null
    }
}

This is the SSE code

import { eventStream } from "remix-utils/sse/server";
import { authenticate } from "../shopify.server";

export async function loader({ request }) {

  const { session } = await authenticate.admin(request);

  let controller = new AbortController();

  request.signal.addEventListener("abort", () => controller.abort());

  let start = Date.now();

  return eventStream(controller.signal, function setup(send) {
    let timer = setInterval(async () => {
      let date = new Date();
      // MAKE DB REQUEST WITH SESSION ID. FETCHS INFO. PASSES IT BACK TO CLIENT
      if (date.getTime() - start > 10000) return controller.abort()

      send({ event: "time", data: "HOLA"});
    }, 1000);

    return function clear() {
      clearInterval(timer);
    };
  });
}

Is there perhaps a way to pass the session id to the SSE loader instead?

What am I doing wrong? Any insight would be much appreciated!