Extend the GraphQL API and socket to accept mobile session tokens alongside existing API tokens. Add OrganizationScope middleware that extracts organization_id from query args for mobile users. Add my_organizations query for mobile app org listing. Reviewed-on: graham/towerops-web#48
75 lines
2 KiB
Elixir
75 lines
2 KiB
Elixir
defmodule ToweropsWeb.Plugs.GraphQLAuth do
|
|
@moduledoc """
|
|
Plug for authenticating GraphQL requests using either API tokens or mobile session tokens.
|
|
|
|
Accepts two token types via Authorization: Bearer <token>:
|
|
- API tokens (prefixed with "towerops_") → sets current_organization_id + current_user
|
|
- Mobile session tokens → sets current_user only (no org_id)
|
|
|
|
Returns 401 Unauthorized if neither token type is valid.
|
|
"""
|
|
|
|
import Phoenix.Controller, only: [json: 2]
|
|
import Plug.Conn
|
|
|
|
alias Towerops.Accounts
|
|
alias Towerops.ApiTokens
|
|
alias Towerops.MobileSessions
|
|
|
|
def init(opts), do: opts
|
|
|
|
def call(conn, _opts) do
|
|
case get_req_header(conn, "authorization") do
|
|
["Bearer " <> token] ->
|
|
authenticate(conn, token)
|
|
|
|
_ ->
|
|
unauthorized(conn, "Missing or invalid Authorization header")
|
|
end
|
|
end
|
|
|
|
defp authenticate(conn, "towerops_" <> _ = token) do
|
|
case ApiTokens.verify_token(token) do
|
|
{:ok, organization_id, user} ->
|
|
conn
|
|
|> assign(:current_organization_id, organization_id)
|
|
|> assign(:current_user, user)
|
|
|
|
{:error, :invalid_token} ->
|
|
unauthorized(conn, "Invalid or expired API token")
|
|
end
|
|
end
|
|
|
|
defp authenticate(conn, token) do
|
|
with {:ok, session} <- validate_mobile_session(token),
|
|
{:ok, user} <- get_user(session.user_id) do
|
|
_ = Task.start(fn -> MobileSessions.touch_session(session) end)
|
|
|
|
assign(conn, :current_user, user)
|
|
else
|
|
{:error, _reason} ->
|
|
unauthorized(conn, "Invalid or expired authentication token")
|
|
end
|
|
end
|
|
|
|
defp validate_mobile_session(token) do
|
|
case MobileSessions.get_session_by_token(token) do
|
|
nil -> {:error, :invalid_token}
|
|
session -> {:ok, session}
|
|
end
|
|
end
|
|
|
|
defp get_user(user_id) do
|
|
case Accounts.get_user(user_id) do
|
|
nil -> {:error, :user_not_found}
|
|
user -> {:ok, user}
|
|
end
|
|
end
|
|
|
|
defp unauthorized(conn, message) do
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> json(%{error: message})
|
|
|> halt()
|
|
end
|
|
end
|