Packages
phoenix_kit
1.7.219
1.7.220
1.7.219
1.7.218
1.7.217
1.7.216
1.7.215
1.7.214
1.7.213
1.7.212
1.7.211
1.7.210
1.7.209
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
Current section
Files
lib/phoenix_kit_web/users/magic_link_registration.ex
defmodule PhoenixKitWeb.Users.MagicLinkRegistration do
@moduledoc """
LiveView for magic link registration completion form.
"""
use PhoenixKitWeb, :live_view
alias PhoenixKit.Users.Auth
alias PhoenixKit.Users.Auth.User
alias PhoenixKit.Users.MagicLinkRegistration
alias PhoenixKit.Users.Referrals
alias PhoenixKit.Utils.Routes
alias PhoenixKitWeb.Users.Auth, as: WebAuth
@impl true
def mount(%{"token" => token}, _session, socket) do
case WebAuth.maybe_redirect_authenticated(socket) do
{:redirect, socket} ->
{:ok, socket}
:cont ->
case MagicLinkRegistration.verify_registration_token(token) do
{:ok, email} ->
# Get referral codes configuration
referral_codes_config = Referrals.get_config()
# Generate username suggestion from email
suggested_username = User.generate_username_from_email(email)
changeset =
Auth.change_user_registration(%User{
email: email,
username: suggested_username
})
# Extract IP address for geolocation
ip_address =
case get_connect_info(socket, :peer_data) do
%{address: {a, b, c, d}} -> "#{a}.#{b}.#{c}.#{d}"
%{address: address} -> to_string(address)
_ -> "unknown"
end
{:ok,
socket
|> assign(:page_title, "Complete Registration")
|> assign(:token, token)
|> assign(:email, email)
|> assign(:ip_address, ip_address)
|> assign(:referral_codes_enabled, referral_codes_config.enabled)
|> assign(:referral_codes_required, referral_codes_config.required)
|> assign(:referral_code, nil)
|> assign(:referral_code_error, nil)
|> assign(:trigger_submit, false)
|> assign(:check_errors, false)
|> assign(:remember_me_available, WebAuth.remember_me_enabled?())
|> assign(:remember_me, WebAuth.remember_me_default?())
|> assign_form(changeset)}
{:error, _} ->
{:ok,
socket
|> put_flash(:error, "Registration link is invalid or has expired.")
|> redirect(to: Routes.path("/users/register"))}
end
end
end
@impl true
def handle_event("validate", %{"user" => user_params} = params, socket) do
referral_code = params["referral_code"]
user_params = form_params(user_params)
# Track the checkbox across re-renders so unticking it sticks.
socket = assign(socket, :remember_me, user_params["remember_me"] == "true")
case validate_referral_code(referral_code, socket) do
{:ok, _} ->
socket =
socket
|> assign(referral_code: referral_code)
|> assign(referral_code_error: nil)
changeset =
%User{email: socket.assigns.email}
|> Auth.change_user_registration(user_params)
|> Map.put(:action, :validate)
{:noreply, assign_form(socket, changeset)}
{:error, error_message} ->
socket =
socket
|> assign(referral_code: referral_code)
|> assign(referral_code_error: error_message)
changeset =
%User{email: socket.assigns.email}
|> Auth.change_user_registration(user_params)
|> Map.put(:action, :validate)
{:noreply, assign_form(socket, changeset)}
end
end
@impl true
def handle_event("save", %{"user" => user_params} = params, socket) do
referral_code = params["referral_code"]
user_params = form_params(user_params)
case validate_referral_code(referral_code, socket) do
{:ok, _validated_code} ->
# Add referral_code to user params
user_params =
if referral_code && String.trim(referral_code) != "" do
Map.put(user_params, "referral_code", referral_code)
else
user_params
end
# Complete registration
case MagicLinkRegistration.complete_registration(
socket.assigns.token,
user_params,
socket.assigns.ip_address
) do
{:ok, user} ->
changeset = Auth.change_user_registration(user)
{:noreply, socket |> assign(trigger_submit: true) |> assign_form(changeset)}
{:error, %Ecto.Changeset{} = changeset} ->
{:noreply, socket |> assign(check_errors: true) |> assign_form(changeset)}
# `complete_registration/3` also returns bare atoms — `:invalid_token`
# once the token expires while the form sits open, and
# `:email_already_exists` if the address is claimed in between.
# Without these clauses either outcome was a CaseClauseError.
{:error, reason} ->
{:noreply,
socket
|> put_flash(:error, completion_error_message(reason))
|> redirect(to: Routes.path("/users/register/magic-link"))}
end
{:error, error_message} ->
socket =
socket
|> assign(referral_code: referral_code)
|> assign(referral_code_error: error_message)
|> assign(check_errors: true)
{:noreply, socket}
end
end
defp completion_error_message(:email_already_exists),
do: "That email is already registered. Please log in instead."
defp completion_error_message(_),
do: "Registration link is invalid or has expired. Please request a new one."
# Only what this form legitimately owns. `registration_changeset/3` also casts
# `custom_fields` and the geo columns — fine for server-side callers, but a
# phx-submit payload is whatever the client sends.
@form_fields ~w(email username password first_name last_name account_type
organization_name user_timezone remember_me return_to)
defp form_params(user_params) when is_map(user_params),
do: Map.take(user_params, @form_fields)
defp form_params(other), do: other
defp assign_form(socket, %Ecto.Changeset{} = changeset) do
form = to_form(changeset, as: "user")
if changeset.valid? do
assign(socket, form: form, check_errors: false)
else
assign(socket, form: form)
end
end
defp validate_referral_code(referral_code, socket) do
cond do
not socket.assigns.referral_codes_enabled ->
{:ok, nil}
socket.assigns.referral_codes_required and
(is_nil(referral_code) or String.trim(referral_code) == "") ->
{:error, "Referral code is required"}
referral_code && String.trim(referral_code) != "" ->
validate_referral_code_value(String.trim(referral_code))
true ->
{:ok, nil}
end
end
defp validate_referral_code_value(code_string) do
case Referrals.get_code_by_string(code_string) do
nil ->
{:error, "Invalid referral code"}
code ->
cond do
not code.status ->
{:error, "This referral code is no longer active"}
Referrals.expired?(code) ->
{:error, "This referral code has expired"}
Referrals.usage_limit_reached?(code) ->
{:error, "This referral code has reached its usage limit"}
true ->
{:ok, code}
end
end
end
end