defmodule ToweropsWeb.MobileQRLive do @moduledoc """ LiveView for displaying QR code for mobile app authentication. Shows a QR code containing a temporary token that can be scanned by the mobile app. Automatically polls to detect when the token has been used and shows success message. """ use ToweropsWeb, :live_view alias Towerops.MobileSessions @impl true def mount(_params, _session, socket) do user = socket.assigns.current_scope.user # Create QR login token {:ok, qr_token} = MobileSessions.create_qr_login_token(user.id) # Start polling to check if token has been completed timer_ref = if connected?(socket) do schedule_check() end socket = socket |> assign(:page_title, t("Mobile App")) |> assign(:qr_token, qr_token) |> assign(:completed, false) |> assign(:mobile_session, nil) |> assign(:timer_ref, timer_ref) {:ok, socket} end @impl true def handle_info(:check_completion, socket) do token = socket.assigns.qr_token.token case MobileSessions.check_qr_login_completed(token) do nil -> # Not completed yet, check if expired if DateTime.after?(DateTime.utc_now(), socket.assigns.qr_token.expires_at) do # Token expired, create a new one user = socket.assigns.current_scope.user {:ok, qr_token} = MobileSessions.create_qr_login_token(user.id) timer_ref = schedule_check() socket = socket |> assign(:qr_token, qr_token) |> assign(:timer_ref, timer_ref) |> put_flash(:info, t("QR code expired, generated a new one")) {:noreply, socket} else # Still valid, check again timer_ref = schedule_check() {:noreply, assign(socket, :timer_ref, timer_ref)} end mobile_session -> # Token was completed! Cancel timer since we're done cancel_timer(socket.assigns.timer_ref) socket = socket |> assign(:completed, true) |> assign(:mobile_session, mobile_session) |> assign(:timer_ref, nil) |> put_flash(:info, t("Mobile device authenticated successfully!")) {:noreply, socket} end end # Catch-all for unexpected messages @impl true def handle_info(_msg, socket), do: {:noreply, socket} @impl true def terminate(_reason, socket) do cancel_timer(socket.assigns[:timer_ref]) :ok end defp schedule_check do Process.send_after(self(), :check_completion, 2000) end defp cancel_timer(nil), do: :ok defp cancel_timer(timer_ref) when is_reference(timer_ref) do Process.cancel_timer(timer_ref) :ok end # Generate QR code data URL using qrcode.show API (public service) defp qr_code_url(token) do # Base URL for the mobile app to handle the token # In production, this would be your custom URL scheme like towerops://qr-login?token=... # For now, we'll just use the token directly data = URI.encode(token) "https://qrcode.tec-it.com/API/QRCode?data=#{data}&backcolor=%23ffffff" end @impl true def render(assigns) do ~H"""
<.header> Link Mobile App <:subtitle> Scan this QR code with the Towerops app on your phone to link it to your account
QR Code for mobile login

This QR code expires in 5 minutes

Waiting for mobile app to scan...

How to link your phone

  1. 1 Open the Towerops app on your iPhone
  2. 2 Tap "Scan QR Code" on the login screen
  3. 3 Point your camera at the QR code above
<.icon name="hero-information-circle" class="h-5 w-5 text-blue-400" />

Don't have the app yet?

Download Towerops from the App Store on your iPhone.

<.icon name="hero-check" class="h-10 w-10 text-green-600 dark:text-green-400" />

Mobile Device Authenticated!

Your mobile device has been successfully authenticated.

Device Name
{@mobile_session.device_name || "Unknown"}
Device OS
{@mobile_session.device_os || "Unknown"}
App Version
{@mobile_session.app_version || "Unknown"}
<.link navigate={~p"/users/settings"} class="text-sm font-medium text-blue-600 hover:text-blue-500 dark:text-blue-400" > Manage mobile sessions →
<.link navigate={~p"/orgs"} class="text-sm font-medium text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white" > ← Back to Organizations
""" end end