Authentication and Authorization in Phoenix Live View
Let’s start with a simple Signup User Ecto.Schema. (I assume you know how to setup Live View. If not create a project with the following command )
mix phx.new management --live
Create a module to generate tokens
Now that we have the schema and token generation modules, we can write interface functions in the context module
We will create a SignupLive which will handle the registration process. For that, Create signup_live.ex and signup_live.html.leex.
In the leex file, add the following code. I have used Bulma css and Font awesome but you can remove the unnecessary code and keep the minimum form fields
In the signup_live.ex add the following contents. The code should be self explanatory, but I’ll explain clearly below.
In the mount/3
callback, we are fetching the changeset from the Accounts
context module to render the form.
The form will emit 2 events.
- validate — It is emitted when input field is blurred. So we are only validating the
params
with thechangeset
with the help ofAccounts.check_registration/1
. In theAccounts.check_registration/1
, changeset action is updated to:insert
. Without this, you will not see any errors in the form - save — It is emitted when the user clicks the submit button. We use this to insert the data into the database. If it fails due to database constraint errors, then we will render the changeset with errors.
If the insert is successful, We will use the Accounts.sign(ManagementWeb.Endpoint, user.id)
to create a token and redirect to Routes.session_path(socket, :create, token)
We haven’t created the controller yet. So, we will create a controller with the filename session_contoller.ex
under the controller directory
In the SessionController, create the following method. We have received the token from the SignupLive process. We can verify the token and fetch user_id from the token. Now, we will create a new token with different expiry which will be stored the browser session store.
_________________________________Example: Auth.login
_________________________________ def login(conn, user_id, remember_me \\ false) do
conn
|> put_session(:token, Accounts.sign(conn, user_id))
|> put_session(:remember_me, remember_me)
|> configure_session(renew: true)
end__________________________________def create(conn, %{"token" => token} = params) do case Accounts.verify(ManagementWeb.Endpoint, token) do
{:ok, user_id} ->
conn
|> Auth.login(user_id)
|> redirect(to: Routes.page_path(conn, :index)) _ -> create(conn, params)
end
enddef create(conn, _params) do
conn
|> put_flash(:error, "error")
|> redirect(to: Routes.signup_path(conn, :index))
end
Finally, lets add get “/session/:token”, SessionController, :create
in the router.
After the token has been assigned in the browser session, All the liveview mounts/3
and handle_event/3
will receive %{“token” => token}
like so
mount(_params, %{“token” => token}, socket)
which we can verify using Accounts.verify/3
to check the validity and get the user_id
inside the LiveView. The same procedure can be used to perform Login as well.
It is better to write a plug to protect the protected routes than verifying the token in every LiveView.
I hope it helps. Thanks