Limen.Socket (limen v0.2.1)

Copy Markdown View Source

Gates WebSocket connections, which bypass Limen.Plug.

Phoenix dispatches sockets before any plug in the endpoint runs, so a client that never passed the HTTP gate could still open a LiveView or channel socket. To close that gap:

  1. when rendering a page, embed a socket token issued for the request (token/2), for example in a meta tag;
  2. send it back as a connect parameter (_limen by default);
  3. check it when the socket connects, with check/3 in a socket's connect/3 callback or with the Limen.LiveView on_mount hook.

Tokens are signed with the keys of an instance, and must be checked by the same instance.

The token is signed with its own key and bound to the client's identity like a pass cookie (by default its prefix, JA4 and user agent, see the :bind option of Limen.Challenge), but it is not a pass: it cannot be used as a cookie. A client whose identity changed since the page was served fails the check, reloads the page, and goes through the HTTP gate again.

Checking needs the peer address, x- headers (for x-forwarded-for and the JA4 header behind a proxy) and the user agent:

socket "/live", Phoenix.LiveView.Socket,
  websocket: [connect_info: [:peer_data, :x_headers, :user_agent, :uri, session: @session]]

Each check is a decision at the :socket stage, emitted and sampled like any other. In dry-run mode failed checks are reported and let through.

Summary

Functions

Checks a socket connection.

The connect parameter the token is expected in.

Issues a socket token for the client of conn.

Functions

check(connect_info, params, opts)

@spec check(map(), map(), keyword()) ::
  {:ok, Limen.Decision.t()} | {:error, Limen.Decision.t()}

Checks a socket connection.

connect_info is what Phoenix passes to connect/3 (see the module documentation for the keys it needs) and params the connect parameters.

Options

  • :instance or :otp_app - the instance checking the token, as for Limen.Plug. Required.
  • :mode - :dry_run or :enforce, overriding the instance's mode.
  • :facts - facts about the connection, as for Limen.put_facts/2, recorded in the decision.
  • :policy - a Limen.Policy whose trust rules are checked first, with the facts: a trusted connection is allowed at the :trust stage, whatever its token and even when its prefix is banned, as the same client's requests are by Limen.Plug.

Returns {:ok, decision} when the connection may proceed (always, in dry-run mode) and {:error, decision} when it must be refused.

param()

@spec param() :: String.t()

The connect parameter the token is expected in.

token(conn, opts \\ [])

@spec token(Plug.Conn.t(), keyword()) :: String.t() | nil

Issues a socket token for the client of conn.

Returns nil when Limen denied the request, in which case the page is not being rendered anyway.

Options

  • :instance or :otp_app - the instance issuing the token. Defaults to the instance that made the decision for the request, and must be given when Limen.Plug made none (on :off and :track routes).