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
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.
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