Curated library of 38 atomic skills, 7 personas, and 1 orchestrator for Elixir and Phoenix development. Organized by category: fundamentals, phoenix, database, testing, auth, infrastructure, quality, security, integrations, tooling, frameworks, personas, and orchestration. Covers core Elixir patterns, Phoenix LiveView, Ecto, OTP, Oban, testing, security, deployment, real-time, and modern tooling (Req, Swoosh, Cachex, Broadway, Ash).
73
91%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Phoenix 1.8 introduced Scope as the new authentication primitive, replacing direct current_user access.
assigns[:current_scope] prevents crashes when unauthenticatedanonymous/0 for the unauthenticated case — return a Scope with user: nildefmodule MyApp.Scope do
defstruct [:user, :role, :permissions, :tenant]
def for_user(%MyApp.Accounts.User{} = user) do
%__MODULE__{
user: user,
role: user.role,
permissions: permissions_for(user.role)
}
end
def anonymous, do: %__MODULE__{user: nil}
def authenticated?(%__MODULE__{user: nil}), do: false
def authenticated?(%__MODULE__{}), do: true
def can?(%__MODULE__{permissions: perms}, action) when is_list(perms), do: action in perms
def can?(%__MODULE__{}, _action), do: false
defp permissions_for(:admin), do: [:read, :write, :delete, :manage_users]
defp permissions_for(:editor), do: [:read, :write]
defp permissions_for(_), do: []
enddefmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
@impl true
def mount(_params, _session, socket) do
scope = socket.assigns.current_scope
if Scope.authenticated?(scope) do
{:ok, assign(socket, :posts, Blog.list_user_posts(scope))}
else
{:ok, push_navigate(socket, to: ~p"/login")}
end
end
@impl true
def handle_event("delete", %{"id" => id}, socket) do
scope = socket.assigns.current_scope
if Scope.can?(scope, :delete) do
# perform delete
{:noreply, socket}
else
{:noreply, put_flash(socket, :error, "Not authorized")}
end
end
end<%= if assigns[:current_scope] && Scope.authenticated?(@current_scope) do %>
<p>Welcome, <%= @current_scope.user.email %></p>
<.link href={~p"/settings"}>Settings</.link>
<% else %>
<.link href={~p"/login"}>Log in</.link>
<% end %>MyApp.Scope with for_user/1, anonymous/0, authenticated?/1, and can?/2 as shown above.on_mount hooks — replace user assignment with scope assignment (see before/after below). Run mix test test/my_app_web/live/ --trace and verify all on_mount tests pass before proceeding.@current_user — update all template references to @current_scope.user; use assigns[:current_scope] for optional access. Run mix test and fix any KeyError or FunctionClauseError failures before continuing.scope instead of user to functions that need auth context. Re-run mix test to confirm no regressions.def on_mount(:require_authenticated_user, _params, session, socket) do
case get_user_from_session(session) do
nil -> {:halt, redirect(socket, to: ~p"/login")}
user -> {:cont, assign(socket, :current_user, user)}
end
enddef on_mount(:require_authenticated_user, _params, session, socket) do
scope = get_scope_from_session(session)
if Scope.authenticated?(scope) do
{:cont, assign(socket, :current_scope, scope)}
else
{:halt, redirect(socket, to: ~p"/login")}
end
endAlways test both authenticated and unauthenticated paths.
defmodule MyApp.ScopeTest do
use ExUnit.Case, async: true
describe "authenticated?/1" do
test "returns true for scope with user" do
scope = %MyApp.Scope{user: build(:user)}
assert Scope.authenticated?(scope) == true
end
test "returns false for anonymous scope" do
assert Scope.authenticated?(Scope.anonymous()) == false
end
end
describe "can?/2" do
test "returns true when permission is present" do
scope = %MyApp.Scope{permissions: [:read, :write]}
assert Scope.can?(scope, :read) == true
end
test "returns false when permission is absent or scope is anonymous" do
assert Scope.can?(%MyApp.Scope{permissions: [:read]}, :delete) == false
assert Scope.can?(Scope.anonymous(), :read) == false
end
end
enddescribe "DashboardLive" do
test "shows dashboard content for authenticated user", %{conn: conn} do
conn = log_in_user(conn, insert(:user))
assert {:ok, _, html} = live(conn, "/dashboard")
assert html =~ "Welcome"
end
test "redirects to login when unauthenticated", %{conn: conn} do
assert {:error, {:redirect, %{to: "/login"}}} = live(conn, "/dashboard")
end
end