Packages

A Fair Source multi-agent runtime with deterministic agent scoring and replayable run history.

Retired package: Deprecated - superseded — operator console moved to the Syntropy app

Current section

Files

Jump to
syntropy lib syntropy_web api_auth.ex
Raw

lib/syntropy_web/api_auth.ex

defmodule SyntropyWeb.ApiAuth do
@moduledoc """
Runtime auth policy for Syntropy's external REST and channel surface.
"""
import Plug.Conn, only: [get_req_header: 2]
@type validation_error :: :missing_token | :invalid_token
@type validation_result :: :ok | {:error, validation_error()}
@spec required?() :: boolean()
def required? do
config()
|> Keyword.get(:required, false)
end
@spec validate_conn(Plug.Conn.t()) :: validation_result()
def validate_conn(conn) do
if required?() do
conn
|> get_req_header("authorization")
|> List.first()
|> extract_bearer()
|> validate_token()
else
:ok
end
end
@spec validate_socket(map()) :: validation_result()
def validate_socket(params) do
if required?() do
params
|> Map.get("token")
|> validate_token()
else
:ok
end
end
@spec error_message(validation_error()) :: String.t()
def error_message(:missing_token), do: "Missing bearer token."
def error_message(:invalid_token), do: "Invalid bearer token."
@spec token() :: String.t() | nil
def token do
config()
|> Keyword.get(:token)
end
defp config do
Application.get_env(:syntropy, __MODULE__, [])
end
defp extract_bearer(header) when is_binary(header) do
case String.split(header, ~r/\s+/, parts: 2, trim: true) do
[scheme, token] when byte_size(token) > 0 ->
if String.downcase(scheme) == "bearer", do: token, else: nil
_other ->
nil
end
end
defp extract_bearer(_header), do: nil
defp validate_token(nil), do: {:error, :missing_token}
defp validate_token(provided_token) when is_binary(provided_token) do
case token() do
expected when is_binary(expected) and byte_size(expected) == byte_size(provided_token) ->
if Plug.Crypto.secure_compare(provided_token, expected) do
:ok
else
{:error, :invalid_token}
end
_other ->
{:error, :invalid_token}
end
end
end