QR device-handoff login ("scan to sign in") for PhoenixKit.
This is the PhoenixKit-side integration of the keyfob
library. Keyfob owns the rendezvous — request lifecycle, expiry,
single-use enforcement, the secret split, PubSub signaling. This module
wires it into PhoenixKit: the internal PubSub, the qr_login_enabled
setting, device-metadata extraction for the confirm screen, and activity
logging on approval.
The flow
- A signed-out browser opens
/users/qr-login(PhoenixKitWeb.Users.QrLogin) and a QR encoding the phone-confirm URL is shown. The LiveView waits. - The user's already-signed-in phone scans it, opens the confirm URL
(
PhoenixKitWeb.Users.QrLoginConfirm), reviews the requesting device, and taps Approve — which callsapprove/2with the phone user's uuid. - Approval mints a one-time login token delivered over PubSub only to
the waiting browser, which navigates to the completion controller
(
PhoenixKitWeb.Users.QrLoginComplete). - The controller calls
consume/1({:ok, user_uuid}, exactly once) and logs that user in with PhoenixKit's own session machinery.
Approval always happens on the trusted (phone) device; the browser gets
nothing until the phone approves. See keyfob's docs for the threat
model (QR-jacking, the secret split).
All keyfob calls are routed through PhoenixKit's internal PubSub
(:phoenix_kit_internal_pubsub) so the desktop LiveView and
the phone confirm LiveView rendezvous on the same bus. The default
Keyfob.Store.ETS (started in PhoenixKit.Supervisor) holds requests.
Summary
Functions
See Keyfob.approve/3. user_ref is the approving user's uuid.
See Keyfob.consume/2. Returns {:ok, user_uuid} exactly once.
See Keyfob.create_request/1. Merges the shared PubSub option.
See Keyfob.deny/2.
Extracts a device-description map from the requesting browser's connected socket, shown verbatim on the phone confirm screen so the human can recognise (or reject) the sign-in.
Whether QR device-handoff login is enabled (setting qr_login_enabled,
default false).
Shared keyfob options — routes every call through PhoenixKit's internal PubSub.
Records a user.qr_login_approved activity for the approving user.
See Keyfob.peek/2.
The PubSub name every keyfob call in this integration uses.
Functions
See Keyfob.approve/3. user_ref is the approving user's uuid.
See Keyfob.consume/2. Returns {:ok, user_uuid} exactly once.
See Keyfob.create_request/1. Merges the shared PubSub option.
See Keyfob.deny/2.
@spec device_meta(Phoenix.LiveView.Socket.t()) :: map()
Extracts a device-description map from the requesting browser's connected socket, shown verbatim on the phone confirm screen so the human can recognise (or reject) the sign-in.
Keys: :browser, :os, :ip — any of which may be absent when the
underlying connect-info is unavailable.
@spec enabled?() :: boolean()
Whether QR device-handoff login is enabled (setting qr_login_enabled,
default false).
@spec keyfob_opts() :: keyword()
Shared keyfob options — routes every call through PhoenixKit's internal PubSub.
Records a user.qr_login_approved activity for the approving user.
Actor and target are the same user (they approved their own sign-in), so no notification is generated — this is an audit-trail entry only. Errors are swallowed: a logging failure must never block the sign-in.
See Keyfob.peek/2.
@spec pubsub() :: atom()
The PubSub name every keyfob call in this integration uses.
See Keyfob.subscribe/2.
See Keyfob.unsubscribe/2.