1.3 KiB · text History bfa2c00
defmodule DiogramosWeb.Api.Auth do
  @moduledoc """
  Bearer-token authentication plug for `/api/v1` routes.

  Reads `Authorization: Bearer <token>`, resolves it via
  `Diogramos.Accounts.fetch_user_by_api_token/1`, and assigns
  `:current_scope` so controllers use the same interface as the browser
  pipeline.

  On missing or invalid credentials it halts with a JSON 401 in the shared
  error shape (see `DiogramosWeb.Api.ErrorJSON`).
  """

  import Plug.Conn
  import Phoenix.Controller, only: [json: 2]

  alias Diogramos.Accounts
  alias Diogramos.Accounts.Scope

  def init(opts), do: opts

  def call(conn, _opts) do
    with [header] <- get_req_header(conn, "authorization"),
         {:ok, token} <- parse_bearer(header),
         {user, token_id} <- Accounts.fetch_user_by_api_token(token) do
      conn
      |> assign(:current_scope, Scope.for_user(user))
      |> assign(:current_api_token_id, token_id)
    else
      _ -> unauthenticated(conn)
    end
  end

  defp parse_bearer("Bearer " <> token) when byte_size(token) > 0, do: {:ok, token}
  defp parse_bearer("bearer " <> token) when byte_size(token) > 0, do: {:ok, token}
  defp parse_bearer(_), do: :error

  defp unauthenticated(conn) do
    conn
    |> put_status(:unauthorized)
    |> json(%{error: %{code: "unauthenticated", message: "Missing or invalid token."}})
    |> halt()
  end
end