Skip to main content

User Authentication

If we are going to have users, users need authentication. Luckily Phoenix comes with a nice generator for implementing basic authentication. It makes a good starting point:

cd apps/elixir_mud_web
mix phx.gen.auth Accounts Player players
ℹ️
If your familiar with the auth generator skip to the next section.

This command will ask you if you wish to generate a LiveView based authentication system. You do, press y, and enter. Then we are off to the races. A ton of files will be generated for us.

So what's going on here? We passed 3 arguments to the phx.gen.auth task:

  • First, we have accounts. This is our context for our users. A context is basically an arbitrarily named domain. So anything account related will go in our accounts context.
  • Second is Player. This is the singular name for our users. This will be the name of our data model.
  • Third, is players. This is the plural name of our users. We are calling our users players. This is used for the database table name and some file naming conventions.

We have a few dozen files generated for us:

  • Several LiveView pages related to user registration, login, email confirmation, password recovery; located at apps/elixir_mud_web/live/...
  • A session controller for handling logins at apps/elixir_mud_web/controllers/player_session_controller.ex
  • Our account context located at apps/elixir_mud/accounts.ex
  • Our data model located at apps/elixir_mud/accounts/player.ex
  • Our authentication logic at apps/elixir_mud_web/players_auth.ex
  • Migrations for our players table at apps/elixir_mud/priv/xxxxxx_create_player_auth_tables.exs
  • Tests for all these things...

The Player Model

The auth generator created a player model for us and a database migration. We need to modify it to better suite our needs.

First in the schema block we will add several fields: first_name, last_name, status.

schema "players" do
  field :first_name, :string
  field :last_name, :string
  field :status, Ecto.Enum, 
    values: [:active, :inactive, :banned], default: :active
  field :email, :string
  field :password, :string, virtual: true, redact: true
  field :hashed_password, :string, redact: true
  field :current_password, :string, virtual: true, redact: true
  field :confirmed_at, :naive_datetime

  timestamps()
end

apps/elixir_mud/accounts/player.ex

In the registration_changeset function we need to add the new fields to the cast function call. This way these new fields can be passed into our model during registration.

def registration_changeset(players, attrs, opts \\ []) do
  players
  |> cast(attrs, [:email, :password, :first_name, :last_name])
  |> validate_email(opts)
  |> validate_password(opts)
end

apps/elixir_mud/accounts/player.ex

Next we need to modify the migration to add these fields to the database.

create table(:players) do
  add :first_name, :string, null: false
  add :last_name, :string, null: false
  add :status, :string, null: false, defualt: "active"
  add :email, :citext, null: false
  add :hashed_password, :string, null: false
  add :confirmed_at, :naive_datetime

  timestamps()
end

apps/elixir_mdu/priv/repo/migration/xxxxxxx_create_player_auth_tables.exs

Sanity Check: Run Tests

Now if we run our tests with mix test we will see all kinds of errors. These are all related to our new fields. The auth generator created tests for the whole authentication system for us, but they don't know about the fields we added.

We can update the accounts fixture to contain these new fields:

def valid_player_attributes(attrs \\ %{}) do
  Enum.into(attrs, %{
    first_name: "Alice",
    last_name: "Allison",
    email: unique_player_email(),
    password: valid_player_password()
  })
end

apps/elixir_mud/test/support/fixtures/accounts_fixture.ex

Now if we run mix test again we have only one failure. This is related to the login form not having inputs for first and last name. Lets add those:

<.input field={@form[:first_name]} label="First name" required />
<.input field={@form[:last_name]} label="Last name" required />
<.input field={@form[:email]} type="email" label="Email" required />
<.input field={@form[:password]} type="password" label="Password" required />

apps/elixir_mud_web/lib/elixir_mud_web/player_registration_live.ex

Now we can run mix test and we should have all passing results.

.....................................
Finished in 0.2 seconds (0.2s async, 0.00s sync)
77 tests, 0 failures

Test Acccount Registration

Lets migrate the database and launch the app and try out the registration system:

mix ecto.migrate
mix phx.server

Visit http://localhost:4000 and you should be able to register a new user, log out, and login.

Vae Victis
Updated on Jun 7, 2025