PhoenixKit Integration Guide

Copy Markdown View Source

For developers using PhoenixKit as a Hex dependency in their Phoenix application.

This guide is designed to help both developers and AI assistants (Claude, Cursor, Copilot, Tidewave MCP, etc.) understand how to integrate and use PhoenixKit effectively.


Table of Contents

  1. Quick Start
  2. Installation
  3. Configuration Reference
  4. Authentication Integration
  5. Troubleshooting

Quick Start

# 1. Verify database is prepared
mix ecto.migrations

# 2. Add to mix.exs
{:phoenix_kit, "~> 1.7"}

# 3. Run installation
mix deps.get
mix phoenix_kit.install
mix compile --force

# 4. Create database if needed
mix ecto.create

# 5. Run migrations
mix ecto.migrate

# 6. Start your server
mix phx.server
# Visit /phoenix_kit/admin

Installation

Step 1: Add Dependency

# mix.exs
defp deps do
  [
    {:phoenix_kit, "~> 1.7"}
  ]
end

Step 2: Install

mix deps.get
mix phoenix_kit.install

The installer will:

  • Detect your Repo automatically (or use --repo to specify)
  • Add configuration to config/config.exs
  • Generate migrations
  • Set up mailer integration

Step 3: Configure

The installer adds this to your config. Customize as needed:

# config/config.exs
config :phoenix_kit,
  parent_app_name: :my_app,
  parent_module: MyApp,
  url_prefix: "/phoenix_kit",
  repo: MyApp.Repo,
  mailer: MyApp.Mailer,
  layouts_module: MyApp.Layouts,
  phoenix_version_strategy: :modern

Step 4: Add Routes

The installer adds something like this to your router.ex. Customize as needed:

# lib/my_app_web/router.ex
import PhoenixKitWeb.Integration

scope "/" do
  pipe_through :browser
  phoenix_kit_routes()
end

Step 5: Run Migrations

mix ecto.migrate

Configuration Reference

Core Settings

SettingTypeDefaultDescription
repomoduleauto-detectedYour Ecto Repo module
mailermodulenilYour Swoosh Mailer module
url_prefixstring"/phoenix_kit"URL prefix for all routes
layouttuplePhoenixKit default{LayoutModule, :template}
root_layouttuplePhoenixKit defaultRoot layout for pages

Authentication Settings

config :phoenix_kit, :password_requirements,
  min_length: 8,
  max_length: 72,
  require_uppercase: false,
  require_lowercase: false,
  require_digit: false,
  require_special: false

Rate Limiting

# Hammer 7.x uses ETS backend via PhoenixKit.Users.RateLimiter.Backend
# No additional :hammer config needed

config :phoenix_kit, PhoenixKit.Users.RateLimiter,
  login_limit: 5,                      # Max login attempts per window
  login_window_ms: 60_000,             # 1 minute window
  magic_link_limit: 3,                 # Max magic link requests per window
  magic_link_window_ms: 300_000,       # 5 minute window
  password_reset_limit: 3,             # Max password reset requests per window
  password_reset_window_ms: 300_000,   # 5 minute window
  registration_limit: 3,               # Max registration attempts per window
  registration_window_ms: 3_600_000,   # 1 hour window
  registration_ip_limit: 10,           # Max registrations per IP per window
  registration_ip_window_ms: 3_600_000 # 1 hour window

Authentication Integration

Access Current User

# In a LiveView
def mount(_params, _session, socket) do
  current_user = socket.assigns[:current_user]
  {:ok, socket}
end

# In a Controller
def index(conn, _params) do
  current_user = conn.assigns[:current_user]
  render(conn, :index)
end

Require Authentication

# In your router
import PhoenixKitWeb.Users.Auth

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

  live "/dashboard", DashboardLive
end

Check User Roles

# Check if user has a role
PhoenixKit.Users.Roles.user_has_role?(user, "Admin")
PhoenixKit.Users.Roles.user_has_role?(user, "Owner")

# Get user's roles
roles = PhoenixKit.Users.Roles.get_user_roles(user)

# Check in templates
<%= if PhoenixKit.Users.Roles.user_has_role?(@current_user, "Admin") do %>
  <.link navigate="/admin">Admin Panel</.link>
<% end %>

Check Module-Level Permissions

PhoenixKit V53+ includes granular permissions that control which admin sections each role can access.

# In a LiveView, use the scope from assigns
scope = socket.assigns.phoenix_kit_current_scope

# Check single permission
Scope.has_module_access?(scope, "billing")        # true/false

# Check multiple permissions
Scope.has_any_module_access?(scope, ["billing", "shop"])  # any granted?
Scope.has_all_module_access?(scope, ["billing", "shop"])  # all granted?

# Check system role (Owner or Admin, not custom roles)
Scope.system_role?(scope)

# Get all granted keys
Scope.accessible_modules(scope)  # MapSet of granted permission keys

Route enforcement: PhoenixKit's phoenix_kit_ensure_admin and phoenix_kit_ensure_module_access on_mount hooks automatically enforce permissions on admin routes. Sidebar navigation is gated per-user.

User Registration

# Register a new user
{:ok, user} = PhoenixKit.Users.Auth.register_user(%{
  email: "user@example.com",
  password: "securepassword123"
})

# First user automatically becomes Owner

Troubleshooting

"Repo not configured"

# Ensure config is set
config :phoenix_kit, repo: MyApp.Repo

"Routes not found"

# Ensure you imported and called the macro
import PhoenixKitWeb.Integration
phoenix_kit_routes()

"Mailer not sending emails"

# Check your mailer is configured
config :my_app, MyApp.Mailer,
  adapter: Swoosh.Adapters.SMTP,
  # ... your SMTP settings

# And PhoenixKit knows about it
config :phoenix_kit, mailer: MyApp.Mailer

"Rate limiting not working"

PhoenixKit uses Hammer 7.x with ETS backend via PhoenixKit.Users.RateLimiter.Backend. No additional Hammer configuration is required. If you need to adjust rate limits:

config :phoenix_kit, PhoenixKit.Users.RateLimiter,
  login_limit: 5,
  login_window_ms: 60_000

Further Reading


For AI Assistants

When helping a developer with PhoenixKit:

  1. PhoenixKit is a Hex dependency - Code lives in deps/phoenix_kit/
  2. Don't modify PhoenixKit files - Create code in the user's app that calls PhoenixKit APIs
  3. Entity names are snake_case - e.g., "contact_form", not "Contact Form"
  4. Field keys are snake_case - e.g., "full_name", not "Full Name"
  5. First user is Owner - First registered user gets the Owner role automatically
  6. Routes are prefixed - Default is /phoenix_kit/, configurable via url_prefix
  7. Permissions are cached in Scope - Use Scope.has_module_access?/2 not raw DB queries
  8. Owner bypasses all permission checks - No DB rows needed for Owner access
  9. Entities use created_by_uuid - The field is created_by_uuid, not created_by
  10. Always use entity.uuid - Never use entity.id for entity operations

Last Updated: 2026-03-02