Current section
Files
Jump to
Current section
Files
lib/ingot_web/live/auth_hooks.ex
defmodule IngotWeb.Live.AuthHooks do
@moduledoc """
LiveView on_mount hooks for authentication and authorization.
Provides hooks that can be used in LiveView modules to require
authentication and check permissions.
## Usage
defmodule MyAppWeb.SomeLive do
use MyAppWeb, :live_view
# Require authentication
on_mount {IngotWeb.Live.AuthHooks, :require_authenticated_user}
# Require specific role
on_mount {IngotWeb.Live.AuthHooks, {:require_role, :admin}}
# Check queue access
on_mount {IngotWeb.Live.AuthHooks, {:require_queue_access, "queue_id"}}
end
## Assigns
The following assigns are set by these hooks:
* `:current_user` - Map containing user_id, email, and roles (set by :require_authenticated_user)
"""
import Phoenix.LiveView
import Phoenix.Component, only: [assign: 3]
alias Ingot.Auth
alias Ingot.AnvilClient
@doc """
on_mount hook for authentication and authorization.
Supports multiple hooks:
- `:require_authenticated_user` - Validates session and assigns current_user
- `{:require_role, role}` - Checks that user has specific role
- `{:require_queue_access, queue_id}` - Verifies user has access to queue
"""
def on_mount(:require_authenticated_user, _params, session, socket) do
session_data = %{
user_id: Map.get(session, "user_id"),
user_email: Map.get(session, "user_email"),
roles: Map.get(session, "roles", []),
expires_at: Map.get(session, "expires_at")
}
case Auth.validate_session(session_data) do
{:ok, validated_session} ->
current_user = %{
id: validated_session.user_id,
email: validated_session.user_email,
roles: validated_session.roles
}
socket =
socket
|> assign(:current_user, current_user)
|> assign(:user_id, current_user.id)
{:cont, socket}
{:error, _reason} ->
socket =
socket
|> put_flash(:error, "You must be logged in to access this page.")
|> redirect(to: "/")
{:halt, socket}
end
end
def on_mount({:require_role, required_role}, _params, _session, socket) do
current_user = socket.assigns[:current_user]
cond do
is_nil(current_user) ->
socket =
socket
|> put_flash(:error, "You must be logged in to access this page.")
|> redirect(to: "/")
{:halt, socket}
Auth.has_role?(current_user.roles, required_role) ->
{:cont, socket}
true ->
socket =
socket
|> put_flash(:error, "You do not have permission to access this page.")
|> redirect(to: "/")
{:halt, socket}
end
end
def on_mount({:require_queue_access, queue_id_or_param}, params, _session, socket)
when is_binary(queue_id_or_param) do
current_user = socket.assigns[:current_user]
if is_nil(current_user) do
socket =
socket
|> put_flash(:error, "You must be logged in to access this page.")
|> redirect(to: "/")
{:halt, socket}
else
# Extract queue_id from params if it's a parameter name
queue_id =
if String.starts_with?(queue_id_or_param, ":") do
param_name = String.trim_leading(queue_id_or_param, ":")
Map.get(params, param_name)
else
queue_id_or_param
end
case AnvilClient.check_queue_access(current_user.id, queue_id) do
{:ok, true} ->
{:cont, assign(socket, :queue_id, queue_id)}
{:ok, false} ->
socket =
socket
|> put_flash(:error, "You do not have access to this queue.")
|> redirect(to: "/")
{:halt, socket}
{:error, _reason} ->
socket =
socket
|> put_flash(:error, "Unable to verify queue access.")
|> redirect(to: "/")
{:halt, socket}
end
end
end
def on_mount(unknown_hook, _params, _session, _socket) do
raise ArgumentError, """
Unknown auth hook: #{inspect(unknown_hook)}
Available hooks:
- :require_authenticated_user
- {:require_role, role}
- {:require_queue_access, queue_id}
"""
end
end