Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support --live to phx.gen.auth #4872

Merged
merged 58 commits into from
Aug 1, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
f012729
generate auth liveviews
bemesa21 Jun 9, 2022
0e2bdd0
conditionally inject liveview authentication code
bemesa21 Jun 17, 2022
4e0c2a1
add auth liveviews tests
bemesa21 Jun 21, 2022
e0aadaa
update failing tests
bemesa21 Jun 21, 2022
8d439cd
fix formatting issues
bemesa21 Jun 23, 2022
480b310
Apply suggestions from code review
bemesa21 Jun 23, 2022
2268c1c
Fix issue with login after registration
bemesa21 Jun 24, 2022
dfff0c9
Update priv/templates/phx.gen.auth/login_live.ex
chrismccord Jun 27, 2022
7e682d4
Replace user menu with function component
chrismccord Jun 29, 2022
87fff3e
show errors live, optionally validate email uniqueness
bemesa21 Jun 27, 2022
cae6dd3
format routes
bemesa21 Jun 30, 2022
cf902c0
generates settings liveview code
bemesa21 Jun 30, 2022
b992b37
generate confirmation liveview
bemesa21 Jun 30, 2022
0ed9d04
fix injection issues
bemesa21 Jun 30, 2022
93af377
rename tests, add doc
bemesa21 Jun 30, 2022
ade1ca5
fix format issues
bemesa21 Jun 30, 2022
5ffc22a
add missing settings live tests
bemesa21 Jun 30, 2022
551f997
change logic to generate files
bemesa21 Jun 30, 2022
4667bbd
fix formatted issues
bemesa21 Jun 30, 2022
c83d6df
fix failing tests
bemesa21 Jun 30, 2022
f573fab
Consolidate login controller
chrismccord Jul 4, 2022
9ce9260
Update tests and bring up to speed with LV 0.18
chrismccord Jul 4, 2022
6171477
formatting
chrismccord Jul 5, 2022
d7e42d8
Use submit_form LiveViewTest function
chrismccord Jul 5, 2022
3829dc9
Fix missing file
chrismccord Jul 5, 2022
beb3a40
Temporary assigns where applicable
chrismccord Jul 6, 2022
66c59fa
Dedup
chrismccord Jul 6, 2022
87be3fd
Docs
chrismccord Jul 6, 2022
abe3347
Simply registration changeset handling
chrismccord Jul 6, 2022
88d8a6b
Update lib/mix/phoenix/schema.ex
chrismccord Jul 6, 2022
d0f92ef
Kill unused function
chrismccord Jul 6, 2022
798df3c
Add forgot password Liveview
bemesa21 Jul 8, 2022
94e2cb6
remove unused function
bemesa21 Jul 8, 2022
28f8ef9
Add ConfirmationInstructionsLive and tests
bemesa21 Jul 8, 2022
36a786a
update test
bemesa21 Jul 8, 2022
b8d0bd9
Inject the user nav directly in layout
chrismccord Jul 13, 2022
03068c0
Fix tests
chrismccord Jul 13, 2022
546f009
Dry up padding indent
chrismccord Jul 13, 2022
03ba277
Update priv/templates/phx.gen.auth/session_controller.ex
chrismccord Jul 13, 2022
ddf497b
add live_session
bemesa21 Jul 13, 2022
9b6a5a5
handle email confirmation token in mount callback
bemesa21 Jul 13, 2022
80f940f
unique email is not validated until changes are applied
bemesa21 Jul 13, 2022
677af57
reasign password changeset
bemesa21 Jul 13, 2022
8c33b8f
apply format
bemesa21 Jul 13, 2022
edee13f
Update priv/templates/phx.gen.auth/schema.ex
bemesa21 Jul 13, 2022
10ad509
Add method to registration form
bemesa21 Jul 13, 2022
2f8283a
live errors for reset password form
bemesa21 Jul 13, 2022
8181717
fix failing test
bemesa21 Jul 13, 2022
0b004df
Remove phx-change from login form
chrismccord Jul 14, 2022
9b9cb9e
interactive prompt for LiveView generation
bemesa21 Jul 14, 2022
1c42da1
update integration tests
bemesa21 Jul 14, 2022
aeba900
update settings live
bemesa21 Jul 18, 2022
4a887b7
update mix.lock
bemesa21 Jul 20, 2022
b379eb8
Bump docs
chrismccord Jul 29, 2022
76278db
Touchup
chrismccord Jul 29, 2022
487b097
Update for verified routes
chrismccord Aug 1, 2022
9828643
Final touch ups
chrismccord Aug 1, 2022
de86e2a
update prompt message
bemesa21 Aug 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
generate confirmation liveview
  • Loading branch information
bemesa21 committed Aug 1, 2022
commit b992b37425148efbfe8a0b12f5d348aa386398df
9 changes: 3 additions & 6 deletions lib/mix/tasks/phx.gen.auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,6 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
{:eex, "schema_token.ex", Path.join([context.dir, "#{schema.singular}_token.ex"])},
{:eex, "auth.ex", Path.join([web_prefix, web_path, "#{schema.singular}_auth.ex"])},
{:eex, "auth_test.exs", Path.join([web_test_prefix, web_path, "#{schema.singular}_auth_test.exs"])},
{:eex, "confirmation_view.ex", Path.join([web_prefix, "views", web_path, "#{schema.singular}_confirmation_view.ex"])},
{:eex, "confirmation_new.html.heex", Path.join([web_prefix, "templates", web_path, "#{schema.singular}_confirmation", "new.html.heex"])},
{:eex, "confirmation_edit.html.heex", Path.join([web_prefix, "templates", web_path, "#{schema.singular}_confirmation", "edit.html.heex"])},
{:eex, "confirmation_controller.ex", Path.join([web_prefix, "controllers", web_path, "#{schema.singular}_confirmation_controller.ex"])},
{:eex, "confirmation_controller_test.exs", Path.join([web_test_prefix, "controllers", web_path, "#{schema.singular}_confirmation_controller_test.exs"])},
{:eex, "session_view.ex", Path.join([web_prefix, "views", web_path, "#{schema.singular}_session_view.ex"])},
{:eex, "session_controller.ex", Path.join([web_prefix, "controllers", web_path, "#{schema.singular}_session_controller.ex"])},
{:eex, "session_controller_test.exs", Path.join([web_test_prefix, "controllers", web_path, "#{schema.singular}_session_controller_test.exs"])},
Expand All @@ -256,7 +251,9 @@ defmodule Mix.Tasks.Phx.Gen.Auth do
{:eex, "reset_password_live.ex", Path.join([web_prefix, "live", web_path, "#{schema.singular}_reset_password_live.ex"])},
{:eex, "reset_password_live_test.exs", Path.join([web_test_prefix, "live", web_path, "#{schema.singular}_reset_password_live_test.exs"])},
{:eex, "settings_live.ex", Path.join([web_prefix, "live", web_path, "#{schema.singular}_settings_live.ex"])},
{:eex, "settings_live_test.exs", Path.join([web_test_prefix, "live", web_path, "#{schema.singular}_settings_live_test.exs"])}
{:eex, "settings_live_test.exs", Path.join([web_test_prefix, "live", web_path, "#{schema.singular}_settings_live_test.exs"])},
{:eex, "confirmation_live.ex", Path.join([web_prefix, "live", web_path, "#{schema.singular}_confirmation_live.ex"])},
{:eex, "confirmation_live_test.exs", Path.join([web_test_prefix, "live", web_path, "#{schema.singular}_confirmation_live_test.exs"])}
]
_ ->
[
Expand Down
102 changes: 102 additions & 0 deletions priv/templates/phx.gen.auth/confirmation_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ConfirmationLive do
use <%= inspect context.web_module %>, :live_view

alias <%= inspect context.module %>

on_mount {<%= inspect auth_module %>, :mount_current_<%= schema.singular %>}
chrismccord marked this conversation as resolved.
Show resolved Hide resolved

bemesa21 marked this conversation as resolved.
Show resolved Hide resolved
def render(%{live_action: :new} = assigns) do
~H"""
<h1>Resend confirmation instructions</h1>

<.form id="resend_confirmation_form" let={f} for={:<%= schema.singular %>} phx-submit="send_instructions">
<%%= label f, :email %>
<%%= email_input f, :email, required: true %>

<div>
<%%= submit "Resend confirmation instructions" %>
</div>
</.form>

<p>
<.link href={Routes.<%= schema.route_helper %>_registration_path(@socket, :new)}>Register</.link> |
<.link href={Routes.<%= schema.route_helper %>_login_path(@socket, :new)}>Log in</.link>
</p>
"""
end

def render(%{live_action: :edit} = assigns) do
~H"""
<h1>Confirm account</h1>

<.form id="confirmation_form" let={_f} for={:<%= schema.singular %>} phx-submit="confirm_account"}>
<div>
<%%= submit "Confirm my account" %>
</div>
</.form>

<p>
<.link href={Routes.<%= schema.route_helper %>_registration_path(@socket, :new)}>Register</.link> |
<.link href={Routes.<%= schema.route_helper %>_login_path(@socket, :new)}>Log in</.link>
</p>
"""
end

def mount(_params, _session, socket) do
{:ok, socket}
end

def handle_params(%{"token" => token}, _uri, socket),
do: {:noreply, assign(socket, :token, token)}

def handle_params(_params, _uri, socket), do: {:noreply, socket}

# Do not log in the <%= schema.singular %> after confirmation to avoid a
# leaked token giving the <%= schema.singular %> access to the account.
def handle_event("confirm_account", _params, socket) do
socket =
case Accounts.confirm_<%= schema.singular %>(socket.assigns.token) do
{:ok, _} ->
socket
|> put_flash(:info, "User confirmed successfully.")
|> redirect(to: "/")

:error ->
# If there is a current <%= schema.singular %> and the account was already confirmed,
# then odds are that the confirmation link was already visited, either
# by some automation or by the <%= schema.singular %> themselves, so we redirect without
# a warning message.
case socket.assigns do
%{current_<%= schema.singular %>: %{confirmed_at: confirmed_at}} when not is_nil(confirmed_at) ->
redirect(socket, to: "/")

%{} ->
socket
|> put_flash(:error, "User confirmation link is invalid or it has expired.")
|> redirect(to: "/")
end
end

{:noreply, socket}
end

def handle_event("send_instructions", %{"<%= schema.singular %>" => %{"email" => email}}, socket) do
if <%= schema.singular %> = <%= inspect context.alias %>.get_<%= schema.singular %>_by_email(email) do
<%= inspect context.alias %>.deliver_<%= schema.singular %>_confirmation_instructions(
<%= schema.singular %>,
&Routes.<%= schema.route_helper %>_confirmation_url(socket, :edit, &1)
)
end

socket =
socket
|> put_flash(
:info,
"If your email is in our system and it has not been confirmed yet, " <>
"you will receive an email with instructions shortly."
)
|> redirect(to: "/")

{:noreply, socket}
end
end
129 changes: 129 additions & 0 deletions priv/templates/phx.gen.auth/confirmation_live_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
defmodule <%= inspect context.web_module %>.<%= inspect Module.concat(schema.web_namespace, schema.alias) %>ConfirmationLiveTest do
use <%= inspect context.web_module %>.ConnCase

import Phoenix.LiveViewTest
import <%= inspect context.module %>Fixtures

alias <%= inspect context.module %>
alias <%= inspect schema.repo %>

setup do
%{<%= schema.singular %>: <%= schema.singular %>_fixture()}
end

describe "Confirm <%= schema.singular %>" do
test "renders confirmation page", %{conn: conn} do
{:ok, _lv, html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :edit, "some-token"))
assert html =~ "<h1>Confirm account</h1>"
end

test "confirms the given token once", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
token =
extract_<%= schema.singular %>_token(fn url ->
<%= inspect context.alias %>.deliver_<%= schema.singular %>_confirmation_instructions(<%= schema.singular %>, url)
end)

{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :edit, token))

result =
lv
|> form("#confirmation_form")
|> render_submit()
|> follow_redirect(conn, "/")

assert {:ok, conn} = result
assert get_flash(conn, :info) =~ "User confirmed successfully"
assert <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id).confirmed_at
refute get_session(conn, :<%= schema.singular %>_token)
assert Repo.all(<%= inspect context.alias %>.<%= inspect schema.alias %>Token) == []

# when not logged in
{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :edit, token))

result =
lv
|> form("#confirmation_form")
|> render_submit()
|> follow_redirect(conn, "/")

assert {:ok, conn} = result
assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"

# when logged in
{:ok, lv, _html} =
build_conn()
|> log_in_<%= schema.singular %>(<%= schema.singular %>)
|> live(Routes.<%= schema.route_helper %>_confirmation_path(conn, :edit, token))

result =
lv
|> form("#confirmation_form")
|> render_submit()
|> follow_redirect(conn, "/")

assert {:ok, conn} = result
refute get_flash(conn, :error)
end

test "does not confirm email with invalid token", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :edit, "invalid-token"))

{:ok, conn} =
lv
|> form("#confirmation_form")
|> render_submit()
|> follow_redirect(conn, "/")

assert get_flash(conn, :error) =~ "User confirmation link is invalid or it has expired"
refute <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id).confirmed_at
end
end

describe "Resend confirmation" do
test "renders the resend confirmation page", %{conn: conn} do
{:ok, _lv, html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :new))
assert html =~ "<h1>Resend confirmation instructions</h1>"
end

test "sends a new confirmation token", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :new))

{:ok, conn} =
lv
|> form("#resend_confirmation_form", <%= schema.singular %>: %{email: <%= schema.singular %>.email})
|> render_submit()
|> follow_redirect(conn, "/")

assert get_flash(conn, :info) =~ "If your email is in our system"
assert Repo.get_by!(<%= inspect context.alias %>.<%= inspect schema.alias %>Token, <%= schema.singular %>_id: <%= schema.singular %>.id).context == "confirm"
end

test "does not send confirmation token if <%= schema.singular %> is confirmed", %{conn: conn, <%= schema.singular %>: <%= schema.singular %>} do
Repo.update!(<%= inspect context.alias %>.<%= inspect schema.alias %>.confirm_changeset(<%= schema.singular %>))

{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :new))

{:ok, conn} =
lv
|> form("#resend_confirmation_form", <%= schema.singular %>: %{email: <%= schema.singular %>.email})
|> render_submit()
|> follow_redirect(conn, "/")

assert get_flash(conn, :info) =~ "If your email is in our system"
refute Repo.get_by(<%= inspect context.alias %>.<%= inspect schema.alias %>Token, <%= schema.singular %>_id: <%= schema.singular %>.id)
end

test "does not send confirmation token if email is invalid", %{conn: conn} do
{:ok, lv, _html} = live(conn, Routes.<%= schema.route_helper %>_confirmation_path(conn, :new))

{:ok, conn} =
lv
|> form("#resend_confirmation_form", <%= schema.singular %>: %{email: "[email protected]"})
|> render_submit()
|> follow_redirect(conn, "/")

assert get_flash(conn, :info) =~ "If your email is in our system"
assert Repo.all(<%= inspect context.alias %>.<%= inspect schema.alias %>Token) == []
end
end
end
6 changes: 4 additions & 2 deletions priv/templates/phx.gen.auth/routes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@
scope <%= router_scope %> do
pipe_through [:browser]

delete "/<%= schema.plural %>/log_out", <%= inspect schema.alias %>SessionController, :delete
delete "/<%= schema.plural %>/log_out", <%= inspect schema.alias %>SessionController, :delete<%= if live? do %>
live "/users/confirm/:token", UserConfirmationLive, :edit
live "/users/confirm", UserConfirmationLive, :new<% else %>
get "/<%= schema.plural %>/confirm", <%= inspect schema.alias %>ConfirmationController, :new
post "/<%= schema.plural %>/confirm", <%= inspect schema.alias %>ConfirmationController, :create
get "/<%= schema.plural %>/confirm/:token", <%= inspect schema.alias %>ConfirmationController, :edit
post "/<%= schema.plural %>/confirm/:token", <%= inspect schema.alias %>ConfirmationController, :update
post "/<%= schema.plural %>/confirm/:token", <%= inspect schema.alias %>ConfirmationController, :update<% end %>
end