Changes profile import endpoint to use standard API token authentication: API Token Changes: - Add user_id to api_tokens table (tracks who created the token) - Update ApiTokens.verify_token/1 to return user along with org_id - Update ApiAuth plug to assign current_user from token Profile Import Changes: - Move endpoint from /api/v1/admin/profiles/import to /api/v1/profiles/import - Check user.is_superuser in controller instead of using RequireSuperuser plug - Use api_v1 pipeline (Bearer token auth) instead of browser session - Update documentation to show API token usage Security: - Only API tokens created by superusers can import profiles - Returns 403 Forbidden if token user is not a superuser - Logs import attempts with user email for audit trail This provides a consistent API experience using Bearer tokens instead of requiring browser session cookies.
53 lines
1.4 KiB
Elixir
53 lines
1.4 KiB
Elixir
defmodule ToweropsWeb.Plugs.ApiAuth do
|
|
@moduledoc """
|
|
Plug for authenticating API requests using Bearer tokens.
|
|
|
|
Expects the Authorization header to contain a Bearer token:
|
|
Authorization: Bearer towerops_xxxxx
|
|
|
|
Sets the following assigns on successful authentication:
|
|
- :current_organization_id - The organization ID the token is scoped to
|
|
- :api_authenticated - true
|
|
|
|
Returns 401 Unauthorized if:
|
|
- No Authorization header is present
|
|
- Token is invalid or expired
|
|
- Token format is incorrect
|
|
"""
|
|
|
|
import Phoenix.Controller, only: [json: 2]
|
|
import Plug.Conn
|
|
|
|
alias Towerops.ApiTokens
|
|
|
|
def init(opts), do: opts
|
|
|
|
def call(conn, _opts) do
|
|
case get_req_header(conn, "authorization") do
|
|
["Bearer " <> token] ->
|
|
authenticate_with_token(conn, token)
|
|
|
|
_ ->
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> json(%{error: "Missing or invalid Authorization header"})
|
|
|> halt()
|
|
end
|
|
end
|
|
|
|
defp authenticate_with_token(conn, token) do
|
|
case ApiTokens.verify_token(token) do
|
|
{:ok, organization_id, user} ->
|
|
conn
|
|
|> assign(:current_organization_id, organization_id)
|
|
|> assign(:current_user, user)
|
|
|> assign(:api_authenticated, true)
|
|
|
|
{:error, :invalid_token} ->
|
|
conn
|
|
|> put_status(:unauthorized)
|
|
|> json(%{error: "Invalid or expired API token"})
|
|
|> halt()
|
|
end
|
|
end
|
|
end
|