mirror of
https://github.com/team-alembic/ash_authentication_phoenix.git
synced 2024-09-19 21:13:52 +12:00
improvement: optional support for routing to register & reset links (#281)
This commit is contained in:
parent
ca1b5d6f58
commit
50f7147d26
6 changed files with 126 additions and 25 deletions
|
@ -375,7 +375,9 @@ defmodule ExampleWeb.Router do
|
|||
get "/", PageController, :home
|
||||
|
||||
# add these lines -->
|
||||
sign_in_route()
|
||||
# Leave out `register_path` and `reset_path` if you don't want to support
|
||||
# user registration and/or password resets respectively.
|
||||
sign_in_route(register_path: "/register", reset_path: "/reset")
|
||||
sign_out_route AuthController
|
||||
auth_routes_for Example.Accounts.User, to: AuthController
|
||||
reset_route []
|
||||
|
|
|
@ -2,7 +2,8 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
use AshAuthentication.Phoenix.Overrides.Overridable,
|
||||
root_class: "CSS class for the root `div` element.",
|
||||
hide_class: "CSS class to apply to hide an element.",
|
||||
show_first: "The form to show on first load. Either `:sign_in` or `:register`.",
|
||||
show_first:
|
||||
"The form to show on first load. Either `:sign_in` or `:register`. Only relevant if paths aren't set for them in the router.",
|
||||
interstitial_class: "CSS class for the `div` element between the form and the button.",
|
||||
sign_in_toggle_text:
|
||||
"Toggle text to display when the sign in form is not showing (or `nil` to disable).",
|
||||
|
@ -32,8 +33,6 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
* `strategy` - The strategy configuration as per
|
||||
`AshAuthentication.Info.strategy/2`. Required.
|
||||
* `overrides` - A list of override modules.
|
||||
* `show_first` - either `:sign_in`, `:register` or `:reset` which controls
|
||||
which form is visible on first load.
|
||||
|
||||
## Slots
|
||||
|
||||
|
@ -43,6 +42,9 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
passed as a slot argument.
|
||||
* `reset_extra` - rendered inside the reset form with the form passed as a
|
||||
slot argument.
|
||||
* `path` - used as the base for links to other pages.
|
||||
* `reset_path` - the path to use for reset links.
|
||||
* `register_path` - the path to use for register links.
|
||||
|
||||
```heex
|
||||
<.live_component
|
||||
|
@ -78,7 +80,9 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
|
||||
@type props :: %{
|
||||
required(:strategy) => AshAuthentication.Strategy.t(),
|
||||
optional(:overrides) => [module]
|
||||
optional(:overrides) => [module],
|
||||
optional(:live_action) => :sign_in | :register,
|
||||
optional(:path) => String.t()
|
||||
}
|
||||
|
||||
slot :sign_in_extra
|
||||
|
@ -127,17 +131,30 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
:register_id,
|
||||
generate_id(subject_name, strategy_name, strategy.register_action_name)
|
||||
)
|
||||
|> assign_new(:show_first, fn -> override_for(assigns.overrides, :show_first, :sign_in) end)
|
||||
|> assign(:hide_class, override_for(assigns.overrides, :hide_class))
|
||||
|> assign(:reset_enabled?, reset_enabled?)
|
||||
|> assign(:register_enabled?, register_enabled?)
|
||||
|> assign(:sign_in_enabled?, !is_nil(override_for(assigns.overrides, :sign_in_toggle_text)))
|
||||
|> assign(:reset_id, reset_id)
|
||||
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|
||||
|> assign_new(:live_action, fn -> :sign_in end)
|
||||
|> assign_new(:path, fn -> "/" end)
|
||||
|> assign_new(:reset_path, fn -> nil end)
|
||||
|> assign_new(:register_path, fn -> nil end)
|
||||
|
||||
show =
|
||||
if assigns[:live_action] == :sign_in && is_nil(assigns[:reset_path]) &&
|
||||
is_nil(assigns[:register_path]) do
|
||||
assigns[:show_first] || :sign_in
|
||||
else
|
||||
assigns[:live_action]
|
||||
end
|
||||
|
||||
assigns = assign(assigns, :show, show)
|
||||
|
||||
~H"""
|
||||
<div class={override_for(@overrides, :root_class)}>
|
||||
<div id={"#{@sign_in_id}-wrapper"} class={unless @show_first == :sign_in, do: @hide_class}>
|
||||
<div id={"#{@sign_in_id}-wrapper"} class={if @show == :sign_in, do: nil, else: @hide_class}>
|
||||
<.live_component
|
||||
:let={form}
|
||||
module={Password.SignInForm}
|
||||
|
@ -155,18 +172,20 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
<div class={override_for(@overrides, :interstitial_class)}>
|
||||
<%= if @reset_enabled? do %>
|
||||
<.toggler
|
||||
message={override_for(@overrides, :reset_toggle_text)}
|
||||
show={@reset_id}
|
||||
hide={[@sign_in_id, @register_id]}
|
||||
message={override_for(@overrides, :reset_toggle_text)}
|
||||
to={@reset_path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<%= if @register_enabled? do %>
|
||||
<.toggler
|
||||
message={override_for(@overrides, :register_toggle_text)}
|
||||
show={@register_id}
|
||||
hide={[@sign_in_id, @reset_id]}
|
||||
message={override_for(@overrides, :register_toggle_text)}
|
||||
to={@register_path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
|
@ -175,7 +194,10 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
</div>
|
||||
|
||||
<%= if @register_enabled? do %>
|
||||
<div id={"#{@register_id}-wrapper"} class={unless @show_first == :register, do: @hide_class}>
|
||||
<div
|
||||
id={"#{@register_id}-wrapper"}
|
||||
class={if @live_action == :register, do: nil, else: @hide_class}
|
||||
>
|
||||
<.live_component
|
||||
:let={form}
|
||||
module={Password.RegisterForm}
|
||||
|
@ -193,17 +215,19 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
<div class={override_for(@overrides, :interstitial_class)}>
|
||||
<%= if @reset_enabled? do %>
|
||||
<.toggler
|
||||
message={override_for(@overrides, :reset_toggle_text)}
|
||||
show={@reset_id}
|
||||
hide={[@sign_in_id, @register_id]}
|
||||
message={override_for(@overrides, :reset_toggle_text)}
|
||||
to={@reset_path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
<%= if @sign_in_enabled? do %>
|
||||
<.toggler
|
||||
message={override_for(@overrides, :sign_in_toggle_text)}
|
||||
show={@sign_in_id}
|
||||
hide={[@register_id, @reset_id]}
|
||||
message={override_for(@overrides, :sign_in_toggle_text)}
|
||||
to={@path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
|
@ -213,7 +237,7 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
<% end %>
|
||||
|
||||
<%= if @reset_enabled? do %>
|
||||
<div id={"#{@reset_id}-wrapper"} class={unless @show_first == :reset, do: @hide_class}>
|
||||
<div id={"#{@reset_id}-wrapper"} class={if @show == :reset, do: nil, else: @hide_class}>
|
||||
<.live_component
|
||||
:let={form}
|
||||
module={Password.ResetForm}
|
||||
|
@ -231,6 +255,7 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
<div class={override_for(@overrides, :interstitial_class)}>
|
||||
<%= if @register_enabled? do %>
|
||||
<.toggler
|
||||
to={@register_path}
|
||||
show={@register_id}
|
||||
hide={[@sign_in_id, @reset_id]}
|
||||
message={override_for(@overrides, :register_toggle_text)}
|
||||
|
@ -239,6 +264,7 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
<% end %>
|
||||
<%= if @sign_in_enabled? do %>
|
||||
<.toggler
|
||||
to={@path}
|
||||
show={@sign_in_id}
|
||||
hide={[@register_id, @reset_id]}
|
||||
message={override_for(@overrides, :sign_in_toggle_text)}
|
||||
|
@ -265,12 +291,20 @@ defmodule AshAuthentication.Phoenix.Components.Password do
|
|||
@doc false
|
||||
@spec toggler(Socket.assigns()) :: Rendered.t() | no_return
|
||||
def toggler(assigns) do
|
||||
if assigns[:to] do
|
||||
~H"""
|
||||
<.link patch={@to} class={override_for(@overrides, :toggler_class)}>
|
||||
<%= @message %>
|
||||
</.link>
|
||||
"""
|
||||
else
|
||||
~H"""
|
||||
<a href="#" phx-click={toggle_js(@show, @hide)} class={override_for(@overrides, :toggler_class)}>
|
||||
<%= @message %>
|
||||
</a>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
||||
defp toggle_js(show, hides, %JS{} = js \\ %JS{}) do
|
||||
show_wrapper = "##{show}-wrapper"
|
||||
|
|
|
@ -42,7 +42,8 @@ defmodule AshAuthentication.Phoenix.Components.Password.RegisterForm do
|
|||
|
||||
@type props :: %{
|
||||
required(:strategy) => AshAuthentication.Strategy.t(),
|
||||
optional(:overrides) => [module]
|
||||
optional(:overrides) => [module],
|
||||
optional(:live_action) => :sign_in | :register
|
||||
}
|
||||
|
||||
@doc false
|
||||
|
|
|
@ -33,6 +33,10 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
|
||||
* `overrides` - A list of override modules.
|
||||
* `otp_app` - The otp app to look for authenticated resources in
|
||||
* `live_action` - The live_action being routed to
|
||||
* `path` - The path to use as the base for links
|
||||
* `reset_path` - The path to use for reset links
|
||||
* `register_path` - The path to use for register links
|
||||
"""
|
||||
|
||||
use Phoenix.LiveComponent
|
||||
|
@ -42,7 +46,10 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
import Slug
|
||||
|
||||
@type props :: %{
|
||||
optional(:overrides) => [module]
|
||||
optional(:overrides) => [module],
|
||||
optional(:path) => String.t(),
|
||||
optional(:reset_path) => String.t(),
|
||||
optional(:register_path) => String.t()
|
||||
}
|
||||
|
||||
@doc false
|
||||
|
@ -68,6 +75,10 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
socket
|
||||
|> assign(:strategies_by_resource, strategies_by_resource)
|
||||
|> assign_new(:overrides, fn -> [AshAuthentication.Phoenix.Overrides.Default] end)
|
||||
|> assign_new(:live_action, fn -> :sign_in end)
|
||||
|> assign_new(:path, fn -> "/" end)
|
||||
|> assign_new(:reset_path, fn -> nil end)
|
||||
|> assign_new(:register_path, fn -> nil end)
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
@ -87,7 +98,11 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
<%= for strategy <- strategies.form do %>
|
||||
<.strategy
|
||||
component={component_for_strategy(strategy)}
|
||||
live_action={@live_action}
|
||||
strategy={strategy}
|
||||
path={@path}
|
||||
reset_path={@reset_path}
|
||||
register_path={@register_path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
|
@ -105,7 +120,11 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
<%= for strategy <- strategies.link do %>
|
||||
<.strategy
|
||||
component={component_for_strategy(strategy)}
|
||||
live_action={@live_action}
|
||||
strategy={strategy}
|
||||
path={@path}
|
||||
reset_path={@reset_path}
|
||||
register_path={@register_path}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
<% end %>
|
||||
|
@ -122,6 +141,10 @@ defmodule AshAuthentication.Phoenix.Components.SignIn do
|
|||
module={@component}
|
||||
id={strategy_id(@strategy)}
|
||||
strategy={@strategy}
|
||||
path={@path}
|
||||
reset_path={@reset_path}
|
||||
register_path={@register_path}
|
||||
live_action={@live_action}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -90,6 +90,14 @@ defmodule AshAuthentication.Phoenix.Router do
|
|||
subject_name = AshAuthentication.Info.authentication_subject_name!(unquote(resource))
|
||||
controller = Keyword.fetch!(unquote(opts), :to)
|
||||
path = Keyword.get(unquote(opts), :path, "/auth")
|
||||
|
||||
path =
|
||||
if String.starts_with?(path, "/") do
|
||||
path
|
||||
else
|
||||
"/" <> path
|
||||
end
|
||||
|
||||
scope_opts = Keyword.get(unquote(opts), :scope_opts, [])
|
||||
|
||||
strategies =
|
||||
|
@ -119,8 +127,11 @@ defmodule AshAuthentication.Phoenix.Router do
|
|||
|
||||
Available options are:
|
||||
|
||||
* `path` the path under which to mount the live-view. Defaults to
|
||||
`"/sign-in"`.
|
||||
* `path` the path under which to mount the sign-in live-view. Defaults to `"/sign-in"`.
|
||||
* `register_path` - the path under which to mount the password strategy's registration live-view.
|
||||
If not set, and registration is supported, registration will use a dynamic toggle and will not be routeable to.
|
||||
* `register_path` - the path under which to mount the password strategy's password reset live-view.
|
||||
If not set, and password reset is supported, password reset will use a dynamic toggle and will not be routeable to.
|
||||
* `live_view` the name of the live view to render. Defaults to
|
||||
`AshAuthentication.Phoenix.SignInLive`.
|
||||
* `as` which is passed to the generated `live` route. Defaults to `:auth`.
|
||||
|
@ -147,6 +158,8 @@ defmodule AshAuthentication.Phoenix.Router do
|
|||
{otp_app, opts} = Keyword.pop(opts, :otp_app)
|
||||
{layout, opts} = Keyword.pop(opts, :layout)
|
||||
{on_mount, opts} = Keyword.pop(opts, :on_mount)
|
||||
{reset_path, opts} = Keyword.pop(opts, :reset_path)
|
||||
{register_path, opts} = Keyword.pop(opts, :register_path)
|
||||
|
||||
{overrides, opts} =
|
||||
Keyword.pop(opts, :overrides, [AshAuthentication.Phoenix.Overrides.Default])
|
||||
|
@ -156,11 +169,17 @@ defmodule AshAuthentication.Phoenix.Router do
|
|||
|> Keyword.put_new(:alias, false)
|
||||
|
||||
quote do
|
||||
scope unquote(path), unquote(opts) do
|
||||
scope "/", unquote(opts) do
|
||||
import Phoenix.LiveView.Router, only: [live: 4, live_session: 3]
|
||||
|
||||
live_session_opts = [
|
||||
session: %{"overrides" => unquote(overrides), "otp_app" => unquote(otp_app)},
|
||||
session: %{
|
||||
"overrides" => unquote(overrides),
|
||||
"otp_app" => unquote(otp_app),
|
||||
"path" => unquote(path),
|
||||
"reset_path" => unquote(reset_path),
|
||||
"register_path" => unquote(register_path)
|
||||
},
|
||||
on_mount: [AshAuthenticationPhoenix.Router.OnLiveViewMount | unquote(on_mount || [])]
|
||||
]
|
||||
|
||||
|
@ -174,7 +193,17 @@ defmodule AshAuthentication.Phoenix.Router do
|
|||
end
|
||||
|
||||
live_session :sign_in, live_session_opts do
|
||||
live("/", unquote(live_view), :sign_in, as: unquote(as))
|
||||
live(unquote(path), unquote(live_view), :sign_in, as: unquote(as))
|
||||
|
||||
if unquote(reset_path) do
|
||||
live(unquote(reset_path), unquote(live_view), :reset, as: :"#{unquote(as)}_reset")
|
||||
end
|
||||
|
||||
if unquote(register_path) do
|
||||
live(unquote(register_path), unquote(live_view), :register,
|
||||
as: :"#{unquote(as)}_register"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -33,10 +33,18 @@ defmodule AshAuthentication.Phoenix.SignInLive do
|
|||
socket
|
||||
|> assign(overrides: overrides)
|
||||
|> assign_new(:otp_app, fn -> nil end)
|
||||
|> assign(:path, session["path"] || "/")
|
||||
|> assign(:reset_path, session["reset_path"])
|
||||
|> assign(:register_path, session["register_path"])
|
||||
|
||||
{:ok, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_params(_, _uri, socket) do
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
@spec render(Socket.assigns()) :: Rendered.t()
|
||||
|
@ -46,6 +54,10 @@ defmodule AshAuthentication.Phoenix.SignInLive do
|
|||
<.live_component
|
||||
module={Components.SignIn}
|
||||
otp_app={@otp_app}
|
||||
live_action={@live_action}
|
||||
path={@path}
|
||||
reset_path={@reset_path}
|
||||
register_path={@register_path}
|
||||
id={override_for(@overrides, :sign_in_id, "sign-in")}
|
||||
overrides={@overrides}
|
||||
/>
|
||||
|
|
Loading…
Reference in a new issue