51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import type { WSEvents } from 'hono/ws'
|
|
|
|
/**
|
|
* Custom application close code (IANA private range 4000-4999) used to
|
|
* signal "auth failed, do not reconnect with this token".
|
|
*
|
|
* Use when:
|
|
* - Rejecting a WebSocket connection because the bearer token in the
|
|
* `?token=` query parameter is missing, invalid, expired, or revoked.
|
|
*
|
|
* Expects:
|
|
* - Server: accept the upgrade first, then close with this code inside
|
|
* `onOpen`. Throwing inside `upgradeWebSocket` produces an HTTP 401
|
|
* that browsers swallow before the connection enters the WS state
|
|
* machine, leaving clients with only `code=1006` — indistinguishable
|
|
* from a transient network drop.
|
|
* - Client: in `onDisconnected`, treat `ev.code === 4001` as a terminal
|
|
* auth failure for the current token and stop the autoReconnect loop
|
|
* until a fresh token rotates the URL.
|
|
*
|
|
* NOTICE:
|
|
* - This constant is duplicated in
|
|
* `packages/stage-ui/src/libs/chat-sync/ws-client.ts` as
|
|
* `WS_CLOSE_UNAUTHORIZED`. The two MUST stay in sync; pulling a shared
|
|
* package in for one constant is more cost than risk here, so we
|
|
* document the contract on both sides instead.
|
|
*/
|
|
export const WS_CLOSE_UNAUTHORIZED = 4001
|
|
|
|
/**
|
|
* Build a `WSEvents` shape that immediately closes the socket with
|
|
* `WS_CLOSE_UNAUTHORIZED` after the upgrade completes.
|
|
*
|
|
* Use when:
|
|
* - A `upgradeWebSocket(async (c) => ...)` factory has determined that
|
|
* the caller is not authenticated and wants to surface that to the
|
|
* client as a structured close code (not an HTTP 401 rejection of
|
|
* the upgrade itself).
|
|
*
|
|
* Returns:
|
|
* - A `WSEvents` object with only `onOpen` populated. The hono/ws helper
|
|
* accepts partial event maps.
|
|
*/
|
|
export function createUnauthorizedWsEvents(): WSEvents {
|
|
return {
|
|
onOpen(_evt, ws) {
|
|
ws.close(WS_CLOSE_UNAUTHORIZED, 'unauthorized')
|
|
},
|
|
}
|
|
}
|