igmarin/elixir-phoenix-skills

phoenix-auth-customization

MANDATORY when extending phx.gen.auth with custom fields. Invoke before adding usernames, profiles, or custom registration fields. Covers migrations, schema updates, fixture updates, form changes, and confirmation patterns. Trigger words: phx.gen.auth, custom fields, registration, username, profile, auth customization.

First seen Jun 20, 2026

Installation

$ npx skills add igmarin/elixir-phoenix-skills --skill phoenix-auth-customization

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from igmarin/elixir-phoenix-skills · top by installs.

npx skills add igmarin/elixir-phoenix-skills

Browse all from igmarin/elixir-phoenix-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 2
License LICENSE
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.0
LicenseMIT
More metadata
version
1.0.0
user-invocable
true

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 6,096 B
  • docs SUMMARY.md 354 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 16 installs

SKILL.md

Phoenix Auth Customization

Use this skill when extending phx.gen.auth with custom fields.

Canonical FP bar: [docs/fcis-engineering-rules.md](../../docs/fcis-engineering-rules.md) — Functional Core, Imperative Shell: pure domain modules; side effects at edges. Authorization decisions should be pure checks on scope/user data; persist only at the edge.

RULES — Follow these with no exceptions

1. Never modify generated auth migrations — create separate migrations for custom fields 2. Update registrationchangeset to cast and validate new fields — don't create a separate changeset 3. Update test fixtures when adding required fields — missing fixture fields cause cryptic test failures 4. Confirm users in test fixtures for password-based auth — set confirmedat or tests will fail 5. Update both the registration form AND the save/2 handler — the form must send the field 6. Use unique_constraint + database unique index for uniqueness — never validate in application code alone

Running phx.gen.auth

# Generate auth with LiveView (recommended)
mix phx.gen.auth Accounts User users

Adding Custom Fields

Step 1: Create a Separate Migration

mix ecto.gen.migration add_username_to_users
defmodule MyApp.Repo.Migrations.AddUsernameToUsers do
  use Ecto.Migration

  def change do
    alter table(:users) do
      add :username, :string, null: false
    end

    create unique_index(:users, [:username])
  end
end
# Run migration and verify success before proceeding
mix ecto.migrate

Step 2: Update the Schema

defmodule MyApp.Accounts.User do
  schema "users" do
    field :email, :string
    field :username, :string  # Add new field
    field :password, :string, virtual: true, redact: true
    field :hashed_password, :string, redact: true
    field :confirmed_at, :utc_datetime

    timestamps()
  end

  # Update registration_changeset to include username
  def registration_changeset(user, attrs, opts \\ []) do
    user
    |> cast(attrs, [:email, :username, :password])
    |> validate_required([:username])
    |> validate_username()
    |> validate_email(opts)
    |> validate_password(opts)
  end

  defp validate_username(changeset) do
    changeset
    |> validate_required([:username])
    |> validate_format(:username, ~r/^[a-zA-Z0-9_]+$/,
      message: "only letters, numbers, and underscores"
    )
    |> validate_length(:username, min: 3, max: 30)
    |> unsafe_validate_unique(:username, MyApp.Repo)
    |> unique_constraint(:username)
  end
end

Step 3: Update Test Fixtures

def user_fixture(attrs \\ %{}) do
  {:ok, user} =
    attrs
    |> Enum.into(%{
      email: "user#{System.unique_integer([:positive])}@example.com",
      username: "user#{System.unique_integer([:positive])}",  # Add username
      password: "hello world!",
      confirmed_at: DateTime.utc_now(:second)  # Confirm for tests
    })
    |> MyApp.Accounts.register_user()

  user
end
# Verify fixtures pass before updating forms
mix test

Step 4: Update the Registration Form

<.simple_form for={@form} phx-change="validate" phx-submit="save">
  <.input field={@form[:username]} type="text" label="Username" />
  <.input field={@form[:email]} type="email" label="Email" />
  <.input field={@form[:password]} type="password" label="Password" />

  <:actions>
    <.button>Register</.button>
  </:actions>
</.simple_form>

Additional Patterns

Profile Fields

For optional fields like displayname or avatarurl, follow the same migration → schema → fixture sequence as above. Differences from required fields:

  • Omit null: false in the migration column definition.
  • Omit validate_required for that field in the changeset.

Confirmation Patterns

phx.gen.auth generates a confirmed_at field and an email-confirmation flow automatically.

  • In tests: set confirmedat: DateTime.utcnow(:second) in fixtures (Rule 4) so tests are not blocked by the confirmation gate.
  • In production: users must click the confirmation link before confirmedat is populated. Guard LiveViews with requireauthenticateduser and check confirmedat explicitly where needed.

Extending Other Changesets

emailchangeset and passwordchangeset follow the same cast-then-validate pattern as registration_changeset. Never add a parallel changeset; extend the existing one.

Common Pitfalls

❌ Don't ✅ Do
Edit the generated phx.gen.auth migration in place Create a separate migration (e.g. addusernameto_users) for custom fields
Create a parallel changeset for the new field Extend registrationchangeset with cast/3 + validate*
Validate uniqueness only in Elixir Pair uniqueconstraint with a database uniqueindex
Forget to update user_fixture/1 Add the required field and confirmed_at to the fixture
Add the field to the schema but not the form Update the form input AND the save/2 handler
Leave confirmed_at nil in test fixtures Set confirmedat: DateTime.utcnow(:second) so password auth tests pass

Integration

Predecessor This Skill Successor
phoenix-liveview-auth phoenix-auth-customization phoenix-authorization-patterns
ecto-changeset-patterns phoenix-auth-customization testing-essentials

Companion skills: phoenix-liveview-auth, ecto-changeset-patterns, testing-essentials