Gates LiveView connections, see Limen.Socket.
In the root layout, embed the socket token:
<meta name="limen-socket" content={Limen.LiveView.token(@conn)} />in app.js, send it when connecting:
const limen = document.querySelector("meta[name='limen-socket']")?.content
const liveSocket = new LiveSocket("/live", Socket, {
params: {_csrf_token: csrfToken, _limen: limen}
})configure the socket's connect info in the endpoint:
socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [:peer_data, :x_headers, :user_agent, :uri, session: @session]]and check connections in the router, with the instance that gates the
pages (see Limen.Socket.check/3 for the options):
live_session :default, on_mount: {Limen.LiveView, otp_app: :my_app} do
...
endWith a :policy, its trust rules are checked first, with the facts
stated by put_facts/2 in an earlier hook, so that clients the HTTP
gate trusts (say, signed-in users) connect whatever their token, as
they browse whatever their prefix:
live_session :default,
on_mount: [MyAppWeb.LimenFacts, {Limen.LiveView, otp_app: :my_app, policy: MyApp.BotPolicy}] do
...
endOnly connected mounts are checked; the initial HTTP render went through
Limen.Plug. A connection that fails the check is redirected to the page
it was mounting, which takes the client back through the HTTP gate (and
its challenge, if needed). LiveView does not give hooks the page's URL,
so it is rebuilt from the route the view is mounted at and its
parameters; a view rendered outside the router goes to /.
The hook also keeps the connection's connect info, which LiveView only
exposes while mounting, so that check_form/3 can check form traps (see
Limen.Trap) in handle_event/3. The connect info describes the
socket, whose URI is the socket's own (/live/websocket), so for views
mounted at the router the hook also follows the page's URL as LiveView
reports it to handle_params/3, and form checks record the page's path.
Summary
Functions
Checks a form submitted to a LiveView for the tells of
Limen.Trap.form_fields/2, see Limen.Trap.check_form/3.
States facts about the connection, as Limen.put_facts/2 does for
requests.
Issues the socket token for the page being rendered, see
Limen.Socket.token/2.
Functions
@spec check_form(Phoenix.LiveView.Socket.t(), map(), keyword()) :: {:ok, Limen.Decision.t()} | {:trapped, Limen.Decision.t()}
Checks a form submitted to a LiveView for the tells of
Limen.Trap.form_fields/2, see Limen.Trap.check_form/3.
def handle_event("save", params, socket) do
case Limen.LiveView.check_form(socket, params, otp_app: :my_app) do
{:ok, _decision} -> save(socket, params)
{:trapped, _decision} -> {:noreply, pretend_it_worked(socket)}
end
endLiveView only gives access to connect info while mounting, so the socket
must have been mounted with the on_mount hook of this module, which
keeps the connect info Limen.Socket documents for later checks.
Options are those of Limen.Trap.check_form/3, where :instance or
:otp_app is required.
@spec put_facts(Phoenix.LiveView.Socket.t(), map() | keyword()) :: Phoenix.LiveView.Socket.t()
States facts about the connection, as Limen.put_facts/2 does for
requests.
Call it from an on_mount hook that runs before this module's, such as
the one that loads the signed-in user:
live_session :default,
on_mount: [{MyAppWeb.UserAuth, :mount_current_user}, MyAppWeb.LimenFacts,
{Limen.LiveView, otp_app: :my_app, policy: MyApp.BotPolicy}] do
...
end
defmodule MyAppWeb.LimenFacts do
def on_mount(:default, _params, _session, socket) do
{:cont, Limen.LiveView.put_facts(socket, signed_in: socket.assigns.current_user != nil)}
end
endThe socket check and check_form/3 record them in their decisions, and
the trust rules of the hook's :policy can use them.
@spec token(Plug.Conn.t(), keyword()) :: String.t() | nil
Issues the socket token for the page being rendered, see
Limen.Socket.token/2.