Skip to content
Back to skills

1517 Plugs Cd698ae5

ASecurity

```elixir defmodule MyAppWeb.Plugs.RequireAuth do import Plug.Conn import Phoenix.Controller

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
securitygo

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 1517-plugs_cd698ae5 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 1517 Plugs Cd698ae5?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 1517 Plugs Cd698ae5
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-1517-plugs-cd698ae5/badge)](https://www.skillsdirectory.com/skills/tools-only-1517-plugs-cd698ae5)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Phoenix Plugs

## Custom Plugs

### Module Plug Structure

```elixir
defmodule MyAppWeb.Plugs.RequireAuth do
  import Plug.Conn
  import Phoenix.Controller

  def init(opts), do: opts

  def call(conn, _opts) do
    if conn.assigns[:current_user] do
      conn
    else
      conn
      |> put_status(:unauthorized)
      |> put_view(MyAppWeb.ErrorJSON)
      |> render(:"401")
      |> halt()  # IMPORTANT: halt after sending response
    end
  end
end
```

### Function Plug

```elixir
defmodule MyAppWeb.UserController do
  plug :load_user when action in [:show, :edit, :update]

  defp load_user(conn, _opts) do
    case Accounts.get_user(conn.params["id"]) do
      {:ok, user} -> assign(conn, :user, user)
      {:error, :not_found} ->
        conn
        |> put_status(:not_found)
        |> render(:not_found)
        |> halt()
    end
  end
end
```

## Authentication Pattern

```elixir
defmodule MyAppWeb.Plugs.LoadCurrentUser do
  import Plug.Conn

  def init(opts), do: opts

  def call(conn, _opts) do
    user_id = get_session(conn, :user_id)

    cond do
      conn.assigns[:current_user] ->
        conn  # Already loaded

      user_id && user = Accounts.get_user!(user_id) ->
        assign(conn, :current_user, user)

      true ->
        assign(conn, :current_user, nil)
    end
  end
end
```

## Authorization Pattern

```elixir
defmodule MyAppWeb.Plugs.RequireAdmin do
  import Plug.Conn
  import Phoenix.Controller

  def init(opts), do: opts

  def call(conn, _opts) do
    user = conn.assigns[:current_user]

    if user && user.admin do
      conn
    else
      conn
      |> put_status(:forbidden)
      |> put_view(MyAppWeb.ErrorJSON)
      |> render(:"403")
      |> halt()
    end
  end
end
```

## Plug Composition

```elixir
# In router
pipeline :authenticated do
  plug MyAppWeb.Plugs.LoadCurrentUser
  plug MyAppWeb.Plugs.RequireAuth
end

pipeline :admin do
  plug MyAppWeb.Plugs.RequireAdmin
end

scope "/admin", MyAppWeb.Admin do
  pipe_through [:browser, :authenticated, :admin]
  # ...
end
```

## Common Mistakes

### Forgetting to Halt

```elixir
# BAD - continues to controller after sending response
def call(conn, _opts) do
  if unauthorized?(conn) do
    conn
    |> send_resp(401, "Unauthorized")
    # Missing halt()! Controller still runs
  else
    conn
  end
end

# GOOD
def call(conn, _opts) do
  if unauthorized?(conn) do
    conn
    |> send_resp(401, "Unauthorized")
    |> halt()
  else
    conn
  end
end
```

### Modifying Halted Conn

```elixir
# BAD - checking after halt
def call(conn, _opts) do
  conn = maybe_halt(conn)
  assign(conn, :data, load_data())  # Runs even if halted!
end

# GOOD - check halted status
def call(conn, _opts) do
  conn = maybe_halt(conn)

  if conn.halted do
    conn
  else
    assign(conn, :data, load_data())
  end
end
```

## Review Questions

1. Do plugs call halt() after sending a response?
2. Is authentication handled via plugs, not controller logic?
3. Are plugs composable and single-purpose?
4. Is halted status checked before further processing?

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.