defmodule EzAuth do
@moduledoc """
Top-level Phoenix and LiveView integration for EzAuth.
"""
import EzAuth.Translations, only: [translate: 1]
alias EzAuth.Config
alias EzAuth.Scopes.UserScope
def on_mount(:assign_current_scope, _params, session, socket) do
{:cont, assign_current_scope(socket, session)}
end
def on_mount(:require_authenticated, _params, session, socket) do
socket = assign_current_scope(socket, session)
if get_in(socket.assigns.current_scope.user) do
{:cont, socket}
else
socket =
socket
|> Phoenix.LiveView.put_flash(:error, translate("You must log in to access this page."))
|> Phoenix.LiveView.redirect(to: Config.sign_in_path())
{:halt, socket}
end
end
defmacro __using__(_opts) do
quote do
import EzAuth.Auth,
only: [
fetch_current_scope: 2,
require_authenticated: 2,
redirect_if_authenticated: 1
]
import EzAuth, only: [auth_routes: 0, auth_routes: 1]
end
end
defmacro auth_routes(opts \\ []) do
handler = Keyword.get(opts, :handler)
strategies = EzAuth.Strategy.supported_strategies()
quote do
EzAuth.__validate_scope__!(__MODULE__)
scope "/auth", alias: false, as: false do
for strategy <- unquote(strategies) do
slug = EzAuth.Strategy.slug(strategy)
post("/#{slug}/request", EzAuth.Dispatcher, :request,
private: %{ez_auth: %{strategy: strategy, handler: unquote(handler)}}
)
for method <- strategy.__meta__(:callback_methods) do
match(method, "/#{slug}/callback", EzAuth.Dispatcher, :callback,
private: %{ez_auth: %{strategy: strategy, handler: unquote(handler)}}
)
end
end
post("/sign-up", EzAuth.Dispatcher, :sign_up,
private: %{ez_auth: %{handler: unquote(handler)}}
)
delete("/sign-out", EzAuth.Dispatcher, :sign_out,
private: %{ez_auth: %{handler: unquote(handler)}}
)
end
end
end
def __validate_scope__!(module) do
if Enum.any?(scopes(module), &problematic?/1) do
raise CompileError,
description: """
auth_routes/1 must be invoked at the router's top level, or inside a top level scope.
Remove the wrapping scope so the auth routes can be correctly registered.
"""
end
end
defp scopes(module) do
List.wrap(
Module.get_attribute(module, :phoenix_top_scopes) ||
Module.get_attribute(module, :phoenix_router_scopes)
)
end
defp problematic?(%{path: p, alias: a, as: as}) do
p not in [nil, "", "/", []] or a not in [nil, []] or as not in [nil, []]
end
defp assign_current_scope(socket, session) do
Phoenix.Component.assign_new(socket, :current_scope, fn ->
case EzAuth.Accounts.get_user_by_session_token(session["user_token"]) do
:error -> nil
user -> UserScope.new(user)
end
end)
end
end