A sticky LiveView a module puts into the admin header, before the notifications bell
(PhoenixKit.Module.header_widgets/0).
A module returns tuples; core normalises them into this struct
(PhoenixKit.ModuleRegistry.all_header_widgets/0) and the admin layout renders each one
with Phoenix.Component.live_render/3 (sticky: true, id: id).
Fields
:live_view- the LiveView module to render.:id- the DOM id of the nested LiveView (not one the admin layout uses itself, seereserved_ids/0). Stable: a sticky LiveView keeps its state (and its PubSub subscriptions) across admin navigation only while the id is the same. Two widgets with one id cannot both be rendered; the first one registered wins.:session_fun-(layout_assigns -> session), called on every render of the layout with the layout's assigns. They include:socket(the parent LiveView's socket),:phoenix_kit_current_scopeand:phoenix_kit_current_user(the user of the scope, filled in when the caller passed only the scope),:current_locale,:current_locale_baseand:impersonating?(seeimpersonating?/1). It returns the session of the nested LiveView: a map with string keys and values that survive being signed into the page, so ids and strings, not structs. Ornil: no widget for this user, nothing is rendered (and no dock) and nothing is logged. Check what the user may see here, where the scope is at hand, rather than after the widget's process has started.:dock-truewhen the widget uses the dock column (#pk-dock, seePhoenixKitWeb.Components.AdminDock). The column exists only while a rendered widget declares it.:module- the module that declared the widget. Filled in by the registry.:session- the result of:session_fun, filled in byPhoenixKit.ModuleRegistry.header_widgets_for/1.
Authority
The session is signed, not encrypted: whoever has the page can read it. Ids and strings the widget needs, never a secret or a token.
The session is all the widget gets: a nested sticky LiveView is mounted without the
live_session's on_mount hooks and without current_scope, and for every signed-in user
of an admin-layout page. The widget has to check the permission itself from the session
(see PhoenixKit.Module.header_widgets/0).
The keys user_token, pk_session_accounts, live_socket_id and phoenix_kit_session_uuid
are reserved (reserved_session_keys/0): they belong to the cookie session, which LiveView
merges under the signed session of the nested LiveView, so a value here would hide the real
one from the session and impersonation checks. They are dropped from the session (one
warning per module and widget, without the values).
session_fun receives the layout's assigns, not the cookie session and not the session
token. A widget that needs the session reference or an impersonation check does it itself
in its connected mount/3: LiveView merges the cookie session from connect_info into the
session of every LiveView, so PhoenixKit.Users.Auth.SessionRef.from_session/1 and
PhoenixKitWeb.Users.MultiSession.impersonated?/1 can be called there on the session
argument (both arrive with the agent-execute PR; this branch does not depend on them).
:impersonating? in the assigns is a hint for rendering: it is read from
scope.multi_session_accounts and is always false while multi-session is off, so it is
not a guarantee. Keep secrets out of the session map: a nested LiveView's session is
signed, not encrypted.
Summary
Types
What PhoenixKit.Module.header_widgets/0 returns, one element per widget.
Functions
True while the account the session acts as is not the root account of the browser's multi-session stack, which is what an impersonation ("log in as this user") looks like.
Normalise one entry of PhoenixKit.Module.header_widgets/0.
The DOM ids of the admin layout that a widget may not use.
The session keys a widget's session function cannot set: they are dropped.
Run the widget's session function against the layout's assigns.
Types
@type entry() :: {live_view :: module(), id :: String.t(), session_fun()} | {live_view :: module(), id :: String.t(), session_fun(), opts :: [{:dock, boolean()}]}
What PhoenixKit.Module.header_widgets/0 returns, one element per widget.
Functions
@spec impersonating?(PhoenixKit.Users.Auth.Scope.t() | nil) :: boolean()
True while the account the session acts as is not the root account of the browser's multi-session stack, which is what an impersonation ("log in as this user") looks like.
The layout passes this as :impersonating? to the session functions. It is an
over-approximation: the stack does not tell an impersonated account from one the user
added with a password, so both read as true. False without a stack (the
multi_session_enabled setting off, or a single account). A widget that must not run for
an operator borrowing a user's session returns nil from its session function when it is
true.
Normalise one entry of PhoenixKit.Module.header_widgets/0.
{live_view, id, session_fun} or {live_view, id, session_fun, opts} with
opts = [dock: boolean]. Returns {:error, reason} (a string) for anything else.
@spec reserved_ids() :: [String.t()]
The DOM ids of the admin layout that a widget may not use.
@spec reserved_session_keys() :: [String.t()]
The session keys a widget's session function cannot set: they are dropped.
@spec resolve_session(t(), map()) :: {:ok, t(), dropped_keys :: [String.t()]} | :skip | {:error, String.t()}
Run the widget's session function against the layout's assigns.
Returns {:ok, widget} with :session filled in, :skip when the function returned
nil (no widget for this user), or {:error, reason} when the function raises or returns
something live_render/3 cannot take (not a map, or a key that is not a string).