defmodulePetalPro.Accounts.Userdo... schema "users"do field :name, :string field :email, :string field :password, :string, virtual: true, redact: true field :hashed_password, :string, redact: true field :confirmed_at, :naive_datetime field :is_admin, :boolean, default: false field :avatar, :string field :last_signed_in_ip, :string field :last_signed_in_datetime, :utc_datetime field :is_subscribed_to_marketing_notifications, :boolean, default: true field :is_suspended, :boolean, default: false field :is_deleted, :boolean, default: false field :is_onboarded, :boolean, default: falsetimestamps()end...end
Users have some extra fields not included by phx.gen.auth:
Authentication
We used phx.gen.auth (email/password) and modified the templates to use Tailwind and Petal Components.
Setting and accessing the current user
Controller actions
For controller actions we use the plug provided by mix phx.gen.auth to set conn.assigns.current_user .
user_auth.ex
defmodulePetalProWeb.UserAuthdo...deffetch_current_user(conn,_opts) do {user_token, conn} =ensure_user_token(conn) user = user_token &&Accounts.get_user_by_session_token(user_token)assign(conn, :current_user, user)end...end
You can see the :fetch_current_user plug used in the :browser pipeline in the router.
If you want to enforce the user then you can use the :require_authenticated_user plug.
router.ex
scope "/",PetalProWebdo pipe_through [:browser, :require_authenticated_user]# Routes that require a logged in user go hereend
Live views
We can't rely on our plugs in live views, since live views connect over web sockets and avoid the traditional request/response lifecycle. However, a live view will have access to the session, which contains the user token set upon login. Hence, in the live view mount we can use the token to find the user_token set in our database for that users session, and from there obtain the logged in user.
Instead of doing this on every live view mount function, we can extract this out into an on_mount function and then apply it in the router, like a mini pipeline.
user_on_mount_hooks.ex
defmodulePetalProWeb.UserOnMountHooksdo...defon_mount(:require_authenticated_user,_params, session, socket) do socket =maybe_assign_user(socket, session)if socket.assigns.current_user do {:cont, socket}else {:halt,redirect(socket, to: Routes.user_session_path(socket, :new))}endenddefpmaybe_assign_user(socket, session) doassign_new(socket, :current_user,fn->get_user(session["user_token"])end)end...end
router.ex
defmodulePetalProWeb.Routerdo... scope "/",PetalProWebdo pipe_through [:browser, :require_authenticated_user] live_session :protected, on_mount: {PetalProWeb.UserOnMountHooks, :require_authenticated_user} do# Live routes that require a logged in user go hereendend...end