Skip to content
Development
Skill

/phoenix-liveview-auth

Use when protecting LiveViews with the generated phx.gen.auth code — on_mount hooks, live_session, mount_current_scope, sudo mode, logout disconnects, auth tests. Not for extra user fields (phoenix-auth-customization) or per-record permissions (phoenix-authorization-patterns).

BOOST
From plugin
elixir-phoenix-guide
16924 skills3 hooks
Install
$ npx -y skills add j-morgan6/elixir-phoenix-guide --skill phoenix-liveview-auth --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/phoenix-liveview-auth

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when protecting LiveViews with the generated phx.gen.auth code — on_mount hooks, live_session, mount_current_scope, sudo mode, logout disconnects, auth tests. Not for extra user fields (phoenix-auth-customization) or per-record permissions (phoenix-authorization-patterns).

SKILL.md

phoenix-liveview-auth.SKILL.md
name: phoenix-liveview-auth
description: "Use when protecting LiveViews with the generated phx.gen.auth code — on_mount hooks, live_session, mount_current_scope, sudo mode, logout disconnects, auth tests. Not for extra user fields (phoenix-auth-customization) or per-record permissions (phoenix-authorization-patterns)."

Phoenix LiveView Authentication

> Verified against Elixir 1.20, Phoenix 1.8, LiveView 1.2, Ecto 3.14 — October 2026

RULES — Follow these with no exceptions

1. **Use the generated `UserAuth`; don't rewrite it** — `mix phx.gen.auth` already ships the plugs, `on_mount` hooks, token reissue, remember-me and socket disconnects. Add routes to its `live_session` blocks; don't re-type `mount_current_scope` or paste pre-1.8 snippets over it. 2. **Use the generated names exactly** — `on_mount` hooks are `:mount_current_scope`, `:require_authenticated` and `:require_sudo_mode`; the router's `live_session`s are `:current_user` and `:require_authenticated_user`; the plug is `:require_authenticated_user`. There is no `:require_authenticated_user` hook and no `:redirect_if_authenticated` hook. 3. **Put login-only LiveViews inside the existing `live_session :require_authenticated_user`** — its scope pipes through the `:require_authenticated_user` plug (HTTP render) *and* runs the `:require_authenticated` hook (websocket mount and live navigation). A `live_session` name may be defined only once in the router. Create a separate one only when its LiveViews need extra `on_mount` hooks (see below). 4. **For guests `@current_scope` is `nil`** — `Scope.for_user(nil)` returns `nil`, not an empty scope. Guard with `@current_scope && @current_scope.user` (or `:if={@current_scope}`). 5. **Never make `/users/log-in` guest-only** — logged-in users are sent there by sudo mode to re-authenticate, so a guest-only guard locks them out of settings. The generator mounts it under `live_session :current_user`; registration redirects logged-in users from its own `mount/3`. 6. **Gate sensitive pages with `:require_sudo_mode`** — `on_mount {MyAppWeb.UserAuth, :require_sudo_mode}` in the LiveView (inside the authenticated `live_session`; it dereferences `current_scope.user`), and re-check `Accounts.sudo_mode?/2` in the action that changes credentials. 7. **Disconnect live sockets when sessions die** — `log_out_user/1` broadcasts `"disconnect"` to the session's `live_socket_id`; after any `Accounts` call that returns expired tokens, call `UserAuth.disconnect_sessions(tokens)`. Never drop `:live_socket_id` from the session. 8. **`get_user_by_session_token/1` returns `{user, token_inserted_at}` or `nil`** — destructure the tuple; never wrap it in a scope directly. 9. **In `on_mount` hooks, fully qualify `Phoenix.LiveView.redirect/2` and `Phoenix.LiveView.put_flash/3`** — `UserAuth` imports `Phoenix.Controller` for its plugs; excluding those imports breaks the plug half. 10. **Test with the generated helpers** — `setup :register_and_log_in_user`, `log_in_user(conn, user, token_authenticated_at: ...)`, and assert `{:error, {:redirect, %{to: path, flash: flash}}}` from `live/2`.

---

What the Generator Gives You

`mix phx.gen.auth Accounts User users --live` (Phoenix 1.8):

| Piece | Name | Used for | |---|---|---| | Plug (in `:browser`) | `:fetch_current_scope_for_user` | assigns `:current_scope` on every request (`nil` for guests) | | Plug | `:require_authenticated_user` | redirects to `~p"/users/log-in"`, stores `:user_return_to` | | `on_mount` | `:mount_current_scope` | assigns `:current_scope` on the socket | | `on_mount` | `:require_authenticated` | halts with redirect to log-in when no user | | `on_mount` | `:require_sudo_mode` | halts unless the user authenticated in the last 10 minutes | | `live_session` | `:current_user` | register, log-in, magic-link confirmation (optional auth) | | `live_session` | `:require_authenticated_user` | settings (login required) | | Function | `disconnect_sessions/1` | broadcasts `"disconnect"` for a list of `%UserToken{}` |

With `--no-live` there are no `on_mount` hooks, `live_session`s, `live_socket_id` or `disconnect_sessions/1`; instead you get a `require_sudo_mode/2` plug and a `redirect_if_user_is_authenticated/2` plug, used only for `/users/register`.

---

Adding Your Routes

Add to the generated blocks. Don't create a parallel `live_session` that replaces the generated hooks.

scope "/", MyAppWeb do
  pipe_through [:browser, :require_authenticated_user]

  live_session :require_authenticated_user,
    on_mount: [{MyAppWeb.UserAuth, :require_authenticated}] do
    live "/users/settings", UserLive.Settings, :edit
    live "/users/settings/confirm-email/:token", UserLive.Settings, :confirm_email

    # your login-only LiveViews
    live "/posts", PostLive.Index, :index
    live "/posts/new", PostLive.Form, :new
  end

  post "/users/update-password", UserSessionController, :update_password
end

scope "/", MyAppWeb do
  pipe_through [:browser]

  live_session :current_user,
    on_mount: [{MyAppWeb.UserAuth, :mount_current_scope}] do
    live "/users/register", UserLive.Registration, :new
    live "/users/log-in", UserLive.Login, :new
    live "/users/log-in/:token", UserLive.Confirmation, :new

    # your pages that work with or without a user. (The generated
    # `get "/", PageController, :home` already owns "/": replace it to make "/" a LiveView.)
    live "/explore", ExploreLive, :index
  end

  post "/users/log-in", UserSessionController, :create
  delete "/users/log-out", UserSessionController, :delete
end

Need an extra check (admin, org membership) for a group of LiveViews? Give them their own `live_session` whose hook list **starts with** `{MyAppWeb.UserAuth, :require_authenticated}` (so the scope is assigned and checked first), and put it in a `scope` that pipes through `[:browser, :require_authenticated_user]`: `live_session :admin, on_mount: [{MyAppWeb.UserAuth, :require_authenticated}, {MyAppWeb.AdminHooks, :require_admin}]

Read more
Ships withelixir-phoenix-guide

· Changelog A Claude Code plugin (with Codex support for the skills) that keeps Claude's Elixir and Phoenix code idiomatic and current.

Get the whole plugin

Other skills on elixir-phoenix-guide.