Current section
Files
Jump to
Current section
Files
lib/codex/auth.ex
defmodule Codex.Auth do
@moduledoc """
API key resolution with `CODEX_API_KEY` → `auth.json` → `OPENAI_API_KEY` precedence.
"""
alias Codex.Auth.Store
alias Codex.Runtime.KeyringWarning
@keyring_warning_key {__MODULE__, :keyring_warning_emitted}
@spec codex_home() :: String.t()
def codex_home do
Codex.Env.get("CODEX_HOME") || Path.join(System.user_home!(), ".codex")
end
@spec auth_paths() :: [String.t()]
def auth_paths do
Store.auth_paths(codex_home(), codex_home_explicit?: !!Codex.Env.get("CODEX_HOME"))
end
@spec infer_auth_mode() :: :api | :chatgpt
def infer_auth_mode do
if api_key_env?(), do: :api, else: stored_auth_mode()
end
@spec api_key() :: String.t() | nil
def api_key do
api_key_env() || stored_api_key()
end
@spec direct_api_key() :: String.t() | nil
def direct_api_key do
api_key() || normalize_string(Codex.Env.get("OPENAI_API_KEY"))
end
@spec chatgpt_access_token() :: String.t() | nil
def chatgpt_access_token do
with {:ok, %Store.Record{tokens: %Store.Tokens{} = tokens} = record} <- load_stored_auth(),
true <- record.auth_mode in [:chatgpt, :chatgpt_auth_tokens] do
tokens.access_token
else
_ -> nil
end
end
defp api_key_env? do
case api_key_env() do
value when is_binary(value) and value != "" -> true
_ -> false
end
end
defp api_key_env do
Codex.Env.get("CODEX_API_KEY")
|> normalize_string()
end
defp stored_api_key do
case load_stored_auth() do
{:ok, %Store.Record{auth_mode: :api_key, openai_api_key: api_key}} ->
api_key
_ ->
nil
end
end
defp normalize_string(nil), do: nil
defp normalize_string(value) when is_binary(value) do
value = String.trim(value)
if value == "", do: nil, else: value
end
defp credentials_store_mode do
cwd =
case File.cwd() do
{:ok, value} -> value
_ -> nil
end
Store.credentials_store_mode(codex_home(), cwd)
end
defp keyring_supported? do
Store.keyring_supported?()
end
defp warn_keyring_unsupported(mode) do
KeyringWarning.warn_once(
@keyring_warning_key,
"codex_sdk does not support keyring auth (cli_auth_credentials_store=#{mode}); remote model fetch is disabled"
)
end
defp stored_auth_mode do
case load_stored_auth() do
{:ok, %Store.Record{} = record} -> Store.infer_auth_mode(record)
_ -> :chatgpt
end
end
defp load_stored_auth do
case credentials_store_mode() do
:keyring ->
warn_keyring_unsupported(:keyring)
{:ok, nil}
:auto ->
if keyring_supported?() do
warn_keyring_unsupported(:auto)
{:ok, nil}
else
Store.load(
codex_home: codex_home(),
codex_home_explicit?: !!Codex.Env.get("CODEX_HOME")
)
end
_ ->
Store.load(codex_home: codex_home(), codex_home_explicit?: !!Codex.Env.get("CODEX_HOME"))
end
end
end