Skip to content

Commit

Permalink
Add email confirmation option
Browse files Browse the repository at this point in the history
  • Loading branch information
alxlion committed Oct 20, 2024
1 parent d9eecfb commit 7476269
Show file tree
Hide file tree
Showing 24 changed files with 757 additions and 269 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ MAIL_FROM_NAME=Claper
# Claper configuration

#ENABLE_ACCOUNT_CREATION=true
#EMAIL_CONFIRMATION=true
#ALLOW_UNLINK_EXTERNAL_PROVIDER=false
#LOGOUT_REDIRECT_URL=https://google.com
#GS_JPG_RESOLUTION=300x300
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- Add duplicate feature on finished events
- Add italian translation (thanks to @loviuz and @albanobattistella)
- Add EMAIL_CONFIRMATION environment variable to disable or enable email confirmation after registration

### Fixes and improvements

Expand All @@ -15,6 +16,7 @@
- Change wording for more options dropdown and access
- Fix dropdown position to be on the front of other elements
- Owner and facilitators of the event can now join the attendee room before the event starts
- Fix email templates

## v2.1.1

Expand Down
5 changes: 5 additions & 0 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ enable_account_creation =
get_var_from_path_or_env(config_dir, "ENABLE_ACCOUNT_CREATION", "true")
|> String.to_existing_atom()

email_confirmation =
get_var_from_path_or_env(config_dir, "EMAIL_CONFIRMATION", "false")
|> String.to_existing_atom()

pool_size = get_int_from_path_or_env(config_dir, "POOL_SIZE", 10)
queue_target = get_int_from_path_or_env(config_dir, "QUEUE_TARGET", 5_000)

Expand Down Expand Up @@ -150,6 +154,7 @@ config :claper, ClaperWeb.Endpoint,

config :claper,
enable_account_creation: enable_account_creation,
email_confirmation: email_confirmation,
allow_unlink_external_provider: allow_unlink_external_provider,
logout_redirect_url: logout_redirect_url

Expand Down
66 changes: 24 additions & 42 deletions lib/claper/accounts/user_notifier.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
defmodule Claper.Accounts.UserNotifier do
import Swoosh.Email
# import Swoosh.Email

alias Claper.Mailer

# Delivers the email using the application mailer.
defp deliver(recipient, subject, body) do
from_name = Application.get_env(:claper, :mail)[:from_name]
from_email = Application.get_env(:claper, :mail)[:from]

email =
new()
|> to(recipient)
|> from({from_name, from_email})
|> subject(subject)
|> text_body(body)

with {:ok, _metadata} <- Mailer.deliver(email) do
{:ok, email}
end
end
# defp deliver(recipient, subject, body) do
# from_name = Application.get_env(:claper, :mail)[:from_name]
# from_email = Application.get_env(:claper, :mail)[:from]

# email =
# new()
# |> to(recipient)
# |> from({from_name, from_email})
# |> subject(subject)
# |> text_body(body)

# with {:ok, _metadata} <- Mailer.deliver(email) do
# {:ok, email}
# end
# end

def deliver_magic_link(email, url) do
email = ClaperWeb.Notifiers.UserNotifier.magic(email, url)
Expand All @@ -40,40 +40,22 @@ defmodule Claper.Accounts.UserNotifier do
Deliver instructions to confirm account.
"""
def deliver_confirmation_instructions(user, url) do
deliver(user.email, "Confirmation instructions", """
==============================
Hi #{user.email},
You can confirm your account by visiting the URL below:
#{url}
If you didn't create an account with us, please ignore this.
email = ClaperWeb.Notifiers.UserNotifier.confirm(user, url)

==============================
""")
with {:ok, _metadata} <- Mailer.deliver(email) do
{:ok, email}
end
end

@doc """
Deliver instructions to reset a user password.
"""
def deliver_reset_password_instructions(user, url) do
deliver(user.email, "Reset password instructions", """
==============================
Hi #{user.email},
You can reset your password by visiting the URL below:
email = ClaperWeb.Notifiers.UserNotifier.reset(user, url)

#{url}
If you didn't request this change, please ignore this.
==============================
""")
with {:ok, _metadata} <- Mailer.deliver(email) do
{:ok, email}
end
end

@doc """
Expand Down
6 changes: 3 additions & 3 deletions lib/claper_web/controllers/user_confirmation_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule ClaperWeb.UserConfirmationController do
{:ok, _} ->
conn
|> put_flash(:info, "User confirmed successfully.")
|> redirect(to: "/")
|> redirect(to: ~p"/users/log_in")

:error ->
# If there is a current user and the account was already confirmed,
Expand All @@ -41,12 +41,12 @@ defmodule ClaperWeb.UserConfirmationController do
# a warning message.
case conn.assigns do
%{current_user: %{confirmed_at: confirmed_at}} when not is_nil(confirmed_at) ->
redirect(conn, to: "/")
redirect(conn, to: ~p"/users/log_in")

%{} ->
conn
|> put_flash(:error, "User confirmation link is invalid or it has expired.")
|> redirect(to: "/")
|> redirect(to: ~p"/")
end
end
end
Expand Down
21 changes: 13 additions & 8 deletions lib/claper_web/controllers/user_registration_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,20 @@ defmodule ClaperWeb.UserRegistrationController do
def create(conn, %{"user" => user_params}) do
case Accounts.register_user(user_params) do
{:ok, user} ->
# {:ok, _} =
# Accounts.deliver_user_confirmation_instructions(
# user,
# &url(~p"/users/confirm/#{&1}")
# )
if Application.get_env(:claper, :email_confirmation) do
{:ok, _} =
Accounts.deliver_user_confirmation_instructions(
user,
&url(~p"/users/confirm/#{&1}")
)

conn
|> put_flash(:info, "User created successfully.")
|> UserAuth.log_in_user(user)
conn
|> redirect(to: ~p"/users/register/confirm")
else
conn
|> put_flash(:info, "User created successfully.")
|> UserAuth.log_in_user(user)
end

{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
Expand Down
12 changes: 11 additions & 1 deletion lib/claper_web/controllers/user_session_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ defmodule ClaperWeb.UserSessionController do
oidc_enabled = Application.get_env(:claper, :oidc)[:enabled]

if user = Accounts.get_user_by_email_and_password(email, password) do
UserAuth.log_in_user(conn, user, user_params)
if Application.get_env(:claper, :email_confirmation) and !user.confirmed_at do
render(conn, "new.html",
error_message:
"You need to confirm your account before logging in. Please check your email for confirmation instructions.",
oidc_provider_name: oidc_provider_name,
oidc_logo_url: oidc_logo_url,
oidc_enabled: oidc_enabled
)
else
UserAuth.log_in_user(conn, user, user_params)
end
else
render(conn, "new.html",
error_message: "Invalid email or password",
Expand Down
6 changes: 5 additions & 1 deletion lib/claper_web/live/event_live/event_form_component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ defmodule ClaperWeb.EventLive.EventFormComponent do
updated_changeset =
case leaders do
[] ->
Events.change_event(socket.assigns.event, Map.put(socket.assigns.changeset.changes, :leaders, []))
Events.change_event(
socket.assigns.event,
Map.put(socket.assigns.changeset.changes, :leaders, [])
)

_ ->
changeset
end
Expand Down
4 changes: 3 additions & 1 deletion lib/claper_web/live/event_live/manage.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,9 @@
bg-white @container rounded-lg p-3 shadow-base transition-all flex flex-col justify-center items-center transform hover:scale-105"
>
<img src="/images/interaction-icons.png" class="w-2/3 @sm:w-1/3" />
<span class="font-semibold text-secondary-800"><%= gettext("Add interaction") %></span>
<span class="font-semibold text-secondary-800">
<%= gettext("Add interaction") %>
</span>
</button>
</div>
</div>
Expand Down
24 changes: 9 additions & 15 deletions lib/claper_web/live/user_live_auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@ defmodule ClaperWeb.UserLiveAuth do
router: ClaperWeb.Router

def on_mount(:default, _params, %{"current_user" => current_user} = _session, socket) do
socket =
socket
|> assign_new(:current_user, fn -> current_user end)
socket = assign_new(socket, :current_user, fn -> current_user end)

{:cont, socket}
cond do
not Application.get_env(:claper, :email_confirmation) ->
{:cont, socket}

# if current_user.confirmed_at do
# socket =
# socket
# |> assign_new(:current_user, fn -> current_user end)
current_user.confirmed_at ->
{:cont, socket}

# {:cont, socket}
# else
# {:halt,
# redirect(socket,
# to: ~p"/users/register/confirm?#{[%{email: current_user.email}]}"
# )}
# end
true ->
{:halt, redirect(socket, to: ~p"/users/register/confirm")}
end
end

def on_mount(:default, _params, _session, socket),
Expand Down
22 changes: 22 additions & 0 deletions lib/claper_web/notifiers/user_notifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,26 @@ defmodule ClaperWeb.Notifiers.UserNotifier do
|> subject(gettext("Update email instructions"))
|> render_body("change.html", %{user: user, url: url})
end

def confirm(user, url) do
new()
|> to(user.email)
|> from(
{Application.get_env(:claper, :mail) |> Keyword.get(:from_name),
Application.get_env(:claper, :mail) |> Keyword.get(:from)}
)
|> subject(gettext("Confirmation instructions"))
|> render_body("confirm.html", %{user: user, url: url})
end

def reset(user, url) do
new()
|> to(user.email)
|> from(
{Application.get_env(:claper, :mail) |> Keyword.get(:from_name),
Application.get_env(:claper, :mail) |> Keyword.get(:from)}
)
|> subject(gettext("Reset password instructions"))
|> render_body("reset.html", %{user: user, url: url})
end
end
8 changes: 4 additions & 4 deletions lib/claper_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ defmodule ClaperWeb.Router do
get("/users/reset_password/:token", UserResetPasswordController, :edit)
post("/users/reset_password/:token", UserResetPasswordController, :update)

get("/users/confirm", UserConfirmationController, :new)
post("/users/confirm", UserConfirmationController, :create)
get("/users/confirm/:token", UserConfirmationController, :update)

get("/users/oidc", UserOidcAuth, :new)
get("/users/oidc/callback", UserOidcAuth, :callback)
end
Expand Down Expand Up @@ -154,9 +158,5 @@ defmodule ClaperWeb.Router do
get("/privacy", PageController, :privacy)

delete("/users/log_out", UserSessionController, :delete)
get("/users/confirm", UserConfirmationController, :new)
post("/users/confirm", UserConfirmationController, :create)
get("/users/confirm/:token", UserConfirmationController, :edit)
post("/users/confirm/:token", UserConfirmationController, :update)
end
end
8 changes: 6 additions & 2 deletions lib/claper_web/templates/layout/email.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@
</tr>
<tr>
<td style="text-align:center;">
<a href="https://claper.co" title="logo" target="_blank">
<a
href={Application.get_env(:claper, ClaperWeb.Endpoint)[:base_url]}
title="logo"
target="_blank"
>
<img
width="60"
src="https://docs.claper.co/assets/logo.png"
src={"#{Application.get_env(:claper, ClaperWeb.Endpoint)[:base_url]}/images/logo.png"}
title="logo"
alt="logo"
/>
Expand Down
57 changes: 57 additions & 0 deletions lib/claper_web/templates/user_notifier/confirm.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<tr>
<td>
<table
width="95%"
border="0"
align="center"
cellpadding="0"
cellspacing="0"
style="max-width:670px;background:#fff; border-radius:3px; text-align:center;-webkit-box-shadow:0 6px 18px 0 rgba(0,0,0,.06);-moz-box-shadow:0 6px 18px 0 rgba(0,0,0,.06);box-shadow:0 6px 18px 0 rgba(0,0,0,.06);"
>
<tr>
<td style="height:40px;">&nbsp;</td>
</tr>
<tr>
<td style="padding:0 35px;">
<h1 style="color:#1e1e2d; font-weight:500; margin:0;font-size:32px;font-family:'Rubik',sans-serif;">
<%= gettext("Confirm account") %>
</h1>
<span style="display:inline-block; vertical-align:middle; margin:29px 0 26px; border-bottom:1px solid #cecece; width:100px;">
</span>
<p style="color:#455056; font-size:15px;line-height:24px; margin:0;">
<%= gettext("You can confirm your account by visiting the URL below") %>
</p>
<a
href={@url}
target="_blank"
style="background:#8611ed;text-decoration:none !important; font-weight:500; margin-top:35px; color:#fff;text-transform:uppercase; font-size:14px;padding:10px 24px;display:inline-block;border-radius:50px;"
>
<%= gettext("CONFIRM ACCOUNT") %>
</a>
<p style="color:#455056; font-size:15px;line-height:24px; margin-top:26px;">
<%= gettext("If you didn't create an account with us, please ignore this.") %>
</p>
</td>
</tr>
<tr>
<td style="height:20px;">&nbsp;</td>
</tr>
<tr>
<td style="font-size: 0.8em; color: #6C6C6C">
<p class="sub">
<%= gettext(
"If you’re having trouble with the button above, copy and paste the URL below into your web browser"
) %>.
</p>
<p class="sub"><%= @url %></p>
</td>
</tr>
<tr>
<td style="height:40px;">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td style="height:20px;">&nbsp;</td>
</tr>
Loading

0 comments on commit 7476269

Please sign in to comment.