Files
2026-07-04 19:27:17 +09:00

100 lines
3.3 KiB
Elixir

defmodule Rsh26poolWeb.Auth do
@moduledoc """
Token-based authentication.
When a user creates or joins a room we generate a random token, persist it on
their `Participant` row, and store that token in the session cookie. On every
request the token is looked up to identify the current participant. This is
the token scheme described in the proposal (a random string whose owner is
recorded in the database, kept in a cookie).
"""
use Rsh26poolWeb, :verified_routes
import Plug.Conn
alias Phoenix.LiveView
alias Rsh26pool.Voting
@token_key "participant_token"
## Plug (controllers) ---------------------------------------------------
@doc "Assigns `:current_participant` from the session token (may be nil)."
def fetch_current_participant(conn, _opts) do
participant = participant_from_token(get_session(conn, @token_key))
assign(conn, :current_participant, participant)
end
@doc "Persists a participant's token in the session and assigns it."
def log_in_participant(conn, participant) do
conn
|> put_session(@token_key, participant.token)
|> assign(:current_participant, participant)
end
@doc "Clears the session token (logout)."
def log_out_participant(conn) do
conn
|> delete_session(@token_key)
|> assign(:current_participant, nil)
end
## LiveView on_mount hooks ---------------------------------------------
@doc """
`on_mount` hooks:
* `:mount_current_participant` — assigns `:current_participant` (may be nil)
* `:require_participant` — requires the viewer to belong to the room in the
URL, otherwise redirects to the enter page
* `:require_owner` — requires the viewer to be the owner of the room
"""
def on_mount(:mount_current_participant, _params, session, socket) do
{:cont, assign_current_participant(socket, session)}
end
def on_mount(:require_participant, params, session, socket) do
socket = assign_current_participant(socket, session)
participant = socket.assigns.current_participant
room_number = params["room_number"]
if participant && participant.room && participant.room.room_number == room_number do
{:cont, socket}
else
socket =
socket
|> LiveView.put_flash(:error, "このルームに参加するには入室してください。")
|> LiveView.redirect(to: ~p"/enter?#{[room: room_number]}")
{:halt, socket}
end
end
def on_mount(:require_owner, params, session, socket) do
socket = assign_current_participant(socket, session)
participant = socket.assigns.current_participant
room_number = params["room_number"]
if participant && participant.room && participant.room.room_number == room_number &&
participant.role == "owner" do
{:cont, socket}
else
socket =
socket
|> LiveView.put_flash(:error, "この管理ページにアクセスする権限がありません。")
|> LiveView.redirect(to: ~p"/")
{:halt, socket}
end
end
defp assign_current_participant(socket, session) do
Phoenix.Component.assign_new(socket, :current_participant, fn ->
participant_from_token(session[@token_key])
end)
end
defp participant_from_token(nil), do: nil
defp participant_from_token(token), do: Voting.get_participant_by_token(token)
end