Pixir.Auth (pixir v0.1.0)

Copy Markdown View Source

Owns the Session's Credential and serializes token refresh (ADR 0002).

A singleton GenServer (one per node). Two credential kinds reach the same Responses Provider:

  • :subscription — a Codex OAuth token (primary), loaded from ~/.pixir/auth.json and auto-refreshed shortly before expiry.
  • :api_key — an OPENAI_API_KEY from the environment (fallback). Never persisted.

Precedence: a stored subscription wins; otherwise OPENAI_API_KEY is used; otherwise the Session is unauthenticated. Because all calls funnel through this one process, refresh is naturally serialized — concurrent Turns never race to refresh.

The public API takes an optional server so tests can run an isolated instance with an injected store path and OAuth module.

Summary

Functions

Return a valid access token / API key, refreshing the subscription if needed.

Whether a usable credential is present (no refresh attempted).

Returns a specification to start this module under a supervisor.

Preferred login entry: browser PKCE first, device-code when the callback port is unavailable. callbacks is a map with

Run the browser PKCE login flow (ADR 0002). on_authorize is called with %{authorize_url, state} so the caller can open the browser; this blocks the caller (not the GenServer) while waiting for the localhost callback. On success the credential is installed and {:ok, status} is returned.

Run the device-code login flow (ADR 0002). on_user_code is called with %{user_code, verification_uri, interval, expires_in} so the caller can display the code; this blocks the caller (not the GenServer) while polling. On success the credential is installed and {:ok, status} is returned.

Forget the subscription credential (falls back to OPENAI_API_KEY if set).

HTTP headers the Provider should send (authorization + account id).

Install a credential (persists subscription credentials).

Human/diagnostic status of the current credential.

Types

credential()

@type credential() :: map()

Functions

access_token(server \\ __MODULE__)

@spec access_token(GenServer.server()) :: {:ok, String.t()} | {:error, map()}

Return a valid access token / API key, refreshing the subscription if needed.

authenticated?(server \\ __MODULE__)

@spec authenticated?(GenServer.server()) :: boolean()

Whether a usable credential is present (no refresh attempted).

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

login(server \\ __MODULE__, callbacks, opts \\ [])

@spec login(GenServer.server(), map(), keyword()) :: {:ok, map()} | {:error, map()}

Preferred login entry: browser PKCE first, device-code when the callback port is unavailable. callbacks is a map with:

  • :on_authorizefn %{authorize_url, state} -> ... end
  • :on_device_codefn %{user_code, verification_uri, interval, expires_in} -> ... end
  • :on_fallback — optional fn message -> ... end when browser login is skipped

login_browser(server \\ __MODULE__, on_authorize, opts \\ [])

@spec login_browser(GenServer.server(), (map() -> any()), keyword()) ::
  {:ok, map()} | {:error, map()}

Run the browser PKCE login flow (ADR 0002). on_authorize is called with %{authorize_url, state} so the caller can open the browser; this blocks the caller (not the GenServer) while waiting for the localhost callback. On success the credential is installed and {:ok, status} is returned.

login_device_code(server \\ __MODULE__, on_user_code, opts \\ [])

@spec login_device_code(GenServer.server(), (map() -> any()), keyword()) ::
  {:ok, map()} | {:error, map()}

Run the device-code login flow (ADR 0002). on_user_code is called with %{user_code, verification_uri, interval, expires_in} so the caller can display the code; this blocks the caller (not the GenServer) while polling. On success the credential is installed and {:ok, status} is returned.

logout(server \\ __MODULE__)

@spec logout(GenServer.server()) :: :ok

Forget the subscription credential (falls back to OPENAI_API_KEY if set).

request_headers(server \\ __MODULE__)

@spec request_headers(GenServer.server()) ::
  {:ok, [{String.t(), String.t()}]} | {:error, map()}

HTTP headers the Provider should send (authorization + account id).

set_credential(server \\ __MODULE__, credential)

@spec set_credential(GenServer.server(), credential()) :: :ok | {:error, map()}

Install a credential (persists subscription credentials).

start_link(opts \\ [])

status(server \\ __MODULE__)

@spec status(GenServer.server()) :: map()

Human/diagnostic status of the current credential.