Pow is an excellent authentication solution for Phoenix HTML applications and APIs. It provides a solid, comprehensive bundle of features out of the box; but what if you need customized behaviour? In this post, we’ll look at how easy it is to customize the default functionality of Pow.
A common area where some customization may be desired is during authentication. This is the scenario we’ll look at today. We want Pow
to handle the concept of a disabled
user. When a user is disabled we don’t want them to be able to log in.
A pretty simple requirement, so let’s get at it!
Getting started
To save some time in getting a boiler plate application set up, we’ll use the code from an earlier post, Phoenix authentication with Pow - Part 1, as a starting point. The code contains a basic Pow
configured application, along with some scaffolding, which we can ignore for the purposes of this post.
So let’s grab the code.
Clone the Repo:
Terminal
git clone -b part-01 https://github.com/riebeekn/phx-auth-with-pow custom-pow
cd custom-pow
We’ll create a branch for today’s work.
Terminal
git checkout -b customize-authentication
And make sure we have our dependencies and database set-up.
Terminal
mix deps.get
Terminal
cd assets && npm install
Note: before running ecto.setup
you may need to update the username / password database settings in dev.config
to match your local postgres settings, i.e.
/config/dev.config …line 69
# Configure your database
config :warehouse, Warehouse.Repo,
username: "postgres",
password: "postgres",
database: "warehouse_dev",
hostname: "localhost",
pool_size: 10
Terminal
cd ..
mix ecto.setup
With that out of the way… let’s see where we are starting from.
Terminal
mix phx.server
If we navigate to http://localhost:4000/ we’ll see our application.
Nothing fancy, basically just the application you would get by running mix phx.new
and setting up Pow
. All we care about for today is the sign in
process so let’s follow the sign up link and create a user to work with.
After signing up, we’ll see that we’ve been logged into our application.
Time to add the disable user functionality.
Adding the disable user functionality
The way our disable functionality will work, is we will have a column in the users
table indicating whether a user is disabled or not. Once we have the column in the database, we’ll update Pow
to take this column into account when authenticating.
Making the database updates
So the first thing we’ll need to do is add a new column to the users
table; let’s create a new migration.
Terminal
mix ecto.gen.migration add_is_disabled_to_users
And update the migration as follows.
/priv/repo/migrations/_add_is_disabled_to_users.exs
defmodule Warehouse.Repo.Migrations.AddIsDisabledToUsers do
use Ecto.Migration
def change do
alter table(:users) do
add :is_disabled, :boolean, null: false, default: false
end
end
end
Pretty simple, we’re adding a new non-null column to users
, and defaulting the value of the column to false
.
Now we’ll run the migration.
Terminal
mix ecto.migrate
And we also need to add the new column to our user
schema.
/lib/warehouse/users/user.ex
defmodule Warehouse.Users.User do
use Ecto.Schema
use Pow.Ecto.Schema
schema "users" do
pow_user_fields()
field :is_disabled, :boolean
timestamps()
end
end
So that’s it for the database and schema updates, onto Pow
!
Updating Pow
So how can we get Pow
to take into account our new column? Turns out this is pretty easy! Looking at the Pow
source, the context module contains some comments which tell us exactly what we need to do:
Nice! So we’re going to want to use pow_authenticate/1
, let’s have a look at the spec
for authenticate
.
Ok, so it looks like the method returns a user
on valid authentication and nil
otherwise.
We now have enough information to implment our disabled user functionality.
The first step is to create the custom context.
Terminal
touch lib/warehouse/users/users.ex
/lib/warehouse/users/users.ex
defmodule Warehouse.Users do
use Pow.Ecto.Context,
repo: Warehouse.Repo,
user: Warehouse.Users.User
def authenticate(params) do
user = pow_authenticate(params)
if user != nil && user.is_disabled do
nil
else
user
end
end
end
What we’ve done is we now check the is_disabled
flag when a user is returned from pow_authenticate\1
. If the flag is set we return nil
instead of the user
… simple!
Before testing things out, we need to update the Pow
configuration.
/config/config.exs …line 20
# Pow configuration
config :warehouse, :pow,
user: Warehouse.Users.User,
repo: Warehouse.Repo,
web_module: WarehouseWeb,
users_context: Warehouse.Users
Testing things out
Let’s start up our server to see our changes in action.
Terminal
mix phx.server
Disable the user
Normally you might create some sort of admin application for managing users, but for the purposes of this post we’ll stick with plain old SQL
. I’m using TablePlus, but you can use any SQL
client you prefer.
SQL
update users set is_disabled = true
where email = 'bob@example.com'
If we now attempt to sign-in again, we’ll see:
Success! Looks like our changes have worked!
If we switch the is_disabled
flag back to false, we’ll once again be able to login.
SQL
update users set is_disabled = false
where email = 'bob@example.com'
Summary
The design of Pow
makes customization of the default behaviour very straight forward and clean. This is fantastic as it means we don’t need to add a bunch of hacks to adapt Pow
to our particular requirements… always a good thing!
Thanks for reading and I hope you enjoyed the post!