Packages

phoenix_kit

1.6.13
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit_web users magic_link.ex
Raw

lib/phoenix_kit_web/users/magic_link.ex

defmodule PhoenixKitWeb.Users.MagicLink do
@moduledoc """
LiveView for magic link authentication.
This LiveView handles the magic link authentication flow:
1. User enters their email address
2. System sends magic link to their email
3. User clicks link to authenticate
The magic link verification is handled by the controller, this LiveView
handles the email input and confirmation flow.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Admin.Presence
alias PhoenixKit.Config
alias PhoenixKit.Mailer
alias PhoenixKit.Users.MagicLink
alias PhoenixKit.Utils.IpAddress
alias PhoenixKit.Utils.Routes
@impl true
def mount(_params, session, socket) do
# Track anonymous visitor session
if connected?(socket) do
session_id = session["live_socket_id"] || generate_session_id()
Presence.track_anonymous(session_id, %{
connected_at: DateTime.utc_now(),
ip_address: IpAddress.extract_from_socket(socket),
user_agent: get_connect_info(socket, :user_agent),
current_page: Routes.path("/users/magic-link")
})
end
form = to_form(%{"email" => ""}, as: "magic_link")
{:ok,
socket
|> assign(:page_title, "Magic Link Login")
|> assign(:form, form)
|> assign(:sent, false)
|> assign(:loading, false)
|> assign(:error, nil)}
end
@impl true
def handle_event("validate", %{"magic_link" => magic_link_params}, socket) do
form = to_form(magic_link_params, as: "magic_link")
{:noreply, assign(socket, form: form)}
end
@impl true
def handle_event("send_magic_link", %{"magic_link" => %{"email" => email}}, socket) do
if valid_email?(email) do
form = to_form(%{"email" => email}, as: "magic_link")
{:noreply,
socket
|> assign(:form, form)
|> assign(:loading, true)
|> assign(:error, nil)
|> send_magic_link_async(email)}
else
form = to_form(%{"email" => email}, as: "magic_link")
{:noreply,
socket
|> assign(:form, form)
|> assign(:error, "Please enter a valid email address")}
end
end
@impl true
def handle_async(:send_magic_link, {:ok, result}, socket) do
case result do
{:ok, _user} ->
{:noreply,
socket
|> assign(:sent, true)
|> assign(:loading, false)
|> put_flash(:info, "Magic link sent! Check your email.")}
{:error, _} ->
# For security, we don't reveal whether the email exists or not
{:noreply,
socket
|> assign(:sent, true)
|> assign(:loading, false)
|> put_flash(:info, "If that email address exists, a magic link has been sent.")}
end
end
@impl true
def handle_async(:send_magic_link, {:exit, _reason}, socket) do
{:noreply,
socket
|> assign(:loading, false)
|> assign(:error, "Failed to send magic link. Please try again.")}
end
# Send magic link email to user and handle response
defp send_magic_link_email_to_user(user, token) do
magic_link_url = MagicLink.magic_link_url(token)
case Mailer.send_magic_link_email(user, magic_link_url) do
{:ok, _} -> {:ok, user}
{:error, reason} -> {:error, reason}
end
end
# Process the magic link sending in the background
defp send_magic_link_async(socket, email) do
Phoenix.LiveView.start_async(socket, :send_magic_link, fn ->
case MagicLink.generate_magic_link(email) do
{:ok, user, token} ->
send_magic_link_email_to_user(user, token)
{:error, :user_not_found} ->
# For security, we simulate the same delay as successful case
Process.sleep(100)
{:error, :user_not_found}
{:error, reason} ->
{:error, reason}
end
end)
end
# Simple email validation
defp valid_email?(email) when is_binary(email) do
String.match?(email, ~r/^[^\s]+@[^\s]+\.[^\s]+$/)
end
defp show_dev_notice? do
Config.mailer_local?()
end
defp generate_session_id do
:crypto.strong_rand_bytes(16) |> Base.encode64()
end
end