skip to content

An investigator's Server-Sent Events feed has streamed for six hours and the credential that authorized it expired two hours ago — what are your options?

level: seniorimportance: must knowfreq 58%

answer

  1. nothing noticed, because nothing looks
  2. three moves, no fourth
  3. ending it is the move with teeth
  4. explain before you end it
  5. the reopen meets an ordinary status

basics

~20 s

Three, and no more: keep emitting to the end of the response, end the response so the client's own reopen is authorized afresh, or emit a terminal application-level event first and then end it. The already-sent status cannot be revised.

solid answer

~40 s

The events kept arriving because nothing in the protocol noticed: the decision was made when the response opened, and the body carries no credential to expire. Once your emitter does notice, it has exactly three honest moves. It can **keep writing** until the response ends anyway — an accepted window equal to the remaining lifetime. It can **end the response**, which is the move with teeth, because the client reopens by itself and that new request gets a fresh decision. Or it can **emit an agreed terminal event first, then end it**, so the receiver can distinguish "you lost access" from "the network dropped" — otherwise both look identical. What it cannot do is turn the `200 OK` it already sent into `401 Unauthorized`, or challenge the receiver in band.

code

http · 2 lines
http
event: streamClosing
data: {"reason":"authorizationExpired","reopen":true}

go deeper

for a junior

The key fact to hold: the stream does not stop when a credential expires, because nothing in the protocol is watching it.

for a middle

Explain why the status cannot be revised mid-response, and that ending the body is what turns into a new, freshly authorized request.

for a senior

Show the operational judgement: re-check on the emitting side at a deliberate interval, emit an agreed terminal event so refusal is distinguishable from an outage, then end the response and answer the reopen truthfully.

for a principal

Argue the trade explicitly — the check interval and the response bound together define the worst-case time a withdrawn entitlement keeps producing data, and each tightening costs reopens across the fleet.

## Why the events were still arriving Nothing was broken. The feed was authorized once, when the server answered the opening GET with `200 OK` and `Content-Type: text/event-stream`, and the body that follows carries no credential and no checkpoint. Expiry is a fact about a credential, not an event on a connection: no timer inside the protocol fires, and the receiving application has no way to volunteer that its credential has lapsed on a channel that runs one way. If your emitter does not look, nobody looks. The same is true of withdrawal rather than expiry. If this investigator is removed from the trial at 14:00, the response opened at 09:00 keeps emitting until something on the emitting side stops writing. ## The three honest moves 1. **Keep emitting until the response ends anyway.** Legitimate when the response already has a bounded lifetime and the feed's sensitivity allows it — but name it for what it is: you have accepted an exposure window equal to the response's remaining life. On a feed of clinical adverse events that is usually the wrong trade. 2. **End the response.** The one move with real force. Because a conforming client reopens by itself, ending the body converts into a brand-new request, and that request is authorized from scratch against whatever it then carries. If the credential was renewed out of band, the feed resumes and the receiver barely notices; if it was not, the reopen is refused at the door. 3. **Emit a terminal application-level event, then end the response.** Same force as (2) plus an explanation. You dispatch an event whose `event:` name both ends have agreed on, carrying a reason in its `data`, and only then finish the body. ## Why the terminal event is worth the trouble When a body simply ends, the stream grammar supplies no reason. A deliberate ending and a cut connection are the *same observation* at the receiver: data stopped. That matters in two ways at a trial site — the console cannot tell the investigator anything useful, and the operator cannot tell a refusal from an outage in their own logs. One agreed event closes both gaps, and costs a few lines. It is a convention, not a protocol feature: a receiver that does not know the name sees an ordinary event and ignores it, which is a safe failure. ## What the reopen then meets That new request is an ordinary one, and it gets an ordinary answer: | situation at reopen | what the request presents | the honest HTTP status | |---|---|---| | credential expired, missing or unreadable | nothing usable to identify the caller | `401 Unauthorized` | | credential fine, this identity no longer has the feed | a valid caller without entitlement | `403 Forbidden` | | credential renewed out of band before the reopen | a usable, entitled caller | `200 OK` and the stream resumes | What the client then does with a non-success answer — reopen again or give up — is the client's own rule, not something this decision controls; assume only that you have answered truthfully. Reporting a refusal as `503 Service Unavailable` or as an empty success is worse than useless: it tells the receiver the service is sick when it is the caller who was refused, and it hides the outcome from everyone reading logs afterwards. ## Choosing, and the thing you cannot choose - For a low-sensitivity feed with an already-bounded response, (1) is defensible and cheap. - For anything where entitlement is the point — a per-trial, per-investigator adverse-event feed — take (3): explain, then end. - Re-checking on the emitting side is what makes (2) and (3) possible at all, and it has a cost: one evaluation per interval per open response. Pick the interval deliberately; it is the real resolution of your enforcement. - Doing the check only at open is also a choice, and its resolution is the whole response lifetime. And the thing no option gives you: there is no mid-response challenge. The status was spent before the first event, header fields cannot be appended once the body runs, and the receiver has no channel to answer on. Every design that looks like an in-band re-authentication turns out, on inspection, to be an application message followed by an ending.

  • Why not just let the stream run to the end of its natural life and re-authorize then?
    That is option one, and it is a real choice — but state its cost: the exposure window equals the response's remaining lifetime, which on an unbounded response is unbounded. It is defensible on a low-sensitivity feed with a short bound, and hard to defend on a per-investigator clinical feed.
  • The terminal event is a convention. What happens to a receiver that does not implement it?
    It sees an event with a name it does not handle and ignores it, then observes the body ending — exactly what it would have observed without the event. The convention degrades safely, which is why it is worth adding even before every consumer understands it.
  • How quickly does ending the response actually stop the flow?
    Immediately for that response — the emitter stops writing and finishes it. What it does not stop is the caller trying again; the enforcement is that the next attempt is decided afresh, so it holds only if the reopen is genuinely refused.

saying these in an interview costs you the question

  • Expecting an expired credential to end an open response by itself.
  • Proposing to send 401 Unauthorized down a response that already returned 200 OK.
  • Answering a refused reopen with 503 Service Unavailable or an empty success.
  • Assuming the receiver can tell a deliberate ending from a dropped connection.
  • Believing a renewed credential reaches a response that is already open.