skip to content

In a Server-Sent Events stream, when is the response authorized, and what re-checks that authorization while it stays open for hours?

level: middleimportance: must knowfreq 66%

answer

  1. one decision, one response
  2. the status precedes the body
  3. no challenge fits in the body
  4. a fresh decision needs a fresh request
  5. ending the body is the instrument

basics

~20 s

Authorization happens once, when the server answers the opening GET with 200 OK and a text/event-stream body. Nothing in the protocol re-checks it afterwards; the body is only data lines, so a fresh decision needs a fresh request.

solid answer

~40 s

A Server-Sent Events stream is an ordinary GET whose response simply never ends, so it is authorized exactly like any other request: once, at the moment the server chooses the status and the header fields. After `200 OK` and `Content-Type: text/event-stream` are on the wire that answer is spent — the response cannot become `401 Unauthorized` later, and the body has nowhere to carry a challenge. A fresh decision only comes from a fresh request, and on this protocol that request is usually one the client makes by itself when the response ends. So the working model is: the decision made at open covers the entire life of that response, and the emitting side's only remaining in-band moves are to emit an application-level event and to end the body.

code

http · 13 lines
http
GET /trials/8841/adverse-events HTTP/1.1
Host: feed.trial.example
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

event: adverseEvent
data: {"subject":"S-114","grade":3}

event: adverseEvent
data: {"subject":"S-207","grade":2}

go deeper

for a junior

Remember the shape: a stream is one GET and one long response, so it is authorized like any request — at the start, once.

for a middle

Be able to explain the ordering: status and header fields go out before the first event, so the answer is already sent and cannot be changed while the body runs.

for a senior

Show that you design around it — a bounded response lifetime, a re-check on the emitting side, and a terminal event before you end the body so the receiver can tell refusal from a dropped connection.

for a principal

Frame it as a staleness budget: the authorization window equals the response lifetime, so what you are really choosing for the fleet is how stale an access decision may get and what each re-decision costs.

## The request is the only place a decision happens Server-Sent Events is not a new protocol and has no handshake of its own. A client issues an ordinary GET carrying `Accept: text/event-stream`, and the server decides — from whatever that request presented — whether this caller may have the feed. It expresses that decision as a **status line**: `200 OK` together with `Content-Type: text/event-stream` means yes, and everything written afterwards is response body. That ordering is the whole lesson. In HTTP the status and the header fields precede the body. By the time the first `data:` line reaches the wire, the authorization answer has already been transmitted and cannot be revised. A stream that stays open for an eight-hour shift at a trial site is still a single response carrying a single decision made in the first few milliseconds of it. ## What holding it open for a shift does to that decision - An investigator's console opens the adverse-event feed at the start of a shift and holds one response for the whole of it. - The decision that let them in was made at one instant, against what that one request carried. - Nothing **in the protocol** re-examines it afterwards: there is no periodic re-check, no per-event check, and no keep-alive that carries a credential back. - The emitting side may of course re-evaluate on its own initiative, on its own schedule — but that is application policy, and its only enforcement action is to stop writing. - The receiving application cannot prove it is still entitled even if it wanted to: the channel runs one way, so it has nothing to send on it. - Every event after the first is therefore written under the original decision, not under a new one. ## Two moves survive, and neither of them is an HTTP status Once the body has started, exactly two things can still be put on that response: 1. **An application-level event.** The emitter can dispatch an event whose `event:` name both ends have agreed means "this stream is about to end, and here is why". That is a convention between your server and your receiver, not a feature of the protocol — a receiver that does not know the name simply sees another event. 2. **The end of the body.** The emitter stops writing and finishes the response. This is the move with teeth: it forces the caller back through a new request if they want the feed again. What cannot be done, no matter how the emitter is written: - The already-sent `200 OK` cannot become `401 Unauthorized` or `403 Forbidden`; a response carries one status. - Header fields cannot be appended once the body has started. - The open response cannot be redirected — `307 Temporary Redirect` is a status too, and statuses are decided at open. - The receiver cannot be asked for anything, because nothing flows back along this response. ## Where a fresh decision actually comes from A conforming client reopens the stream by itself once the response ends, replaying the request it retained. That new request is authorized from scratch against whatever it carries at that moment. So the re-authorization you get on this protocol is a side effect of the response ending — not something the open response can request. | moment | who acts | what is decided | |---|---|---| | the opening GET | server | whether this caller gets the feed, expressed as the status line | | every event afterwards | server writes, client reads | nothing — the body carries data, never decisions | | the response ending | the emitter, or the network | the original decision stops applying | | the client's next request | server | a fresh decision, on whatever that request now carries | ## What this changes in a design - **Treat the response's lifetime as the lifetime of the decision.** If responses are unbounded, an authorization made at 09:00 is still in force at 18:00. - **If you need the decision refreshed, end the response.** There is no gentler instrument, and the client's own reopen turns the ending into a re-authorization. - **Tell the receiver why, before you end it.** Without a terminal application-level event, a deliberate ending and a dropped network are the same observation. - **Do not design a mid-response challenge.** Anything that looks like one is really an application message plus an ending. - **Re-checking on the emitting side is legitimate and costs you something** — one evaluation per interval per open response — so choose the interval knowingly.

  • The credential is still valid but this investigator's access to the feed is withdrawn — does the open stream stop?
    Not by itself. Nothing re-evaluates access while the response is open, so events keep arriving until something on the emitting side decides to stop writing and end the body. Whatever withdrew the access has to reach the process holding that response; the protocol carries no signal to it.
  • Can a server put an HTTP status inside the event stream to say that authorization has lapsed?
    No. The status line was sent before the first event, and the body is only field lines such as `event:` and `data:`. What the emitter can send is an application-level event whose name the receiver understands, and then end the response. That is an agreement between your two ends, not a protocol feature.
  • If the decision is made once, what is the honest way to describe a stream's authorization window?
    As equal to the life of the response. A decision taken at open is in force until that response ends, so the window is however long you allow one response to live, plus nothing. That is the number to quote in a review, and the only way to shrink it is to bound the response.

A visitor pass checked once at the door and never again while you are inside. The guard's only remaining move is to walk you back out, not to re-examine the badge you already used.

saying these in an interview costs you the question

  • Believing the server can still answer 401 Unauthorized once the body has started.
  • Thinking each dispatched event is authorized again as it is written.
  • Assuming an expired credential makes an open response stop on its own.
  • Expecting a mid-response challenge the receiving application can answer.
  • Treating a still-connected client as proof the decision has been re-validated.