Current section
Files
Jump to
Current section
Files
lib/ccxt/ws/auth/expiry.ex
defmodule CCXT.WS.Auth.Expiry do
@moduledoc """
Pure functions for computing auth session expiry timing.
Used by the generated WS Adapter to determine when to schedule
`:auth_expired` timer messages. TTL sources (in priority order):
1. **Response-level** — auth response includes `expires_in` (e.g., Deribit),
returned as `{:ok, %{ttl_ms: N}}` from `handle_auth_response/2`
2. **Config-level** — spec's `ws.auth.auth_ttl_ms` key (static override)
3. **None** — no TTL available, no timer scheduled
## Example
auth_meta = %{ttl_ms: 900_000}
auth_config = %{pattern: :jsonrpc_linebreak}
ttl_ms = Expiry.compute_ttl_ms(auth_meta, auth_config)
# => 900_000
delay = Expiry.schedule_delay_ms(ttl_ms)
# => 720_000 (80% safety margin)
"""
# Schedule re-auth at 80% of TTL to avoid racing the server's expiry
@default_safety_margin 0.80
# Cap at 24 hours — no auth session should go longer without refresh
@max_auth_ttl_ms 86_400_000
@doc """
Resolves effective TTL in milliseconds from auth metadata and config.
Response-level TTL (from `auth_meta.ttl_ms`) takes priority over
config-level TTL (from `auth_config[:auth_ttl_ms]`).
Returns `nil` when no TTL source is available.
"""
@spec compute_ttl_ms(map() | nil, map() | nil) :: pos_integer() | nil
def compute_ttl_ms(auth_meta, auth_config) do
response_ttl =
case auth_meta do
%{ttl_ms: ttl} when is_integer(ttl) and ttl > 0 -> ttl
_ -> nil
end
config_ttl =
case auth_config do
%{auth_ttl_ms: ttl} when is_integer(ttl) and ttl > 0 -> ttl
_ -> nil
end
response_ttl || config_ttl
end
@doc """
Computes the delay before scheduling `:auth_expired`, applying a safety margin.
Applies `#{@default_safety_margin * 100}%` safety margin (re-auth before server expires)
and caps at #{div(@max_auth_ttl_ms, 3_600_000)}h.
Returns `nil` for `nil` or non-positive TTL inputs (no timer should be scheduled).
"""
@spec schedule_delay_ms(pos_integer() | nil) :: pos_integer() | nil
def schedule_delay_ms(nil), do: nil
def schedule_delay_ms(ttl_ms) when is_integer(ttl_ms) and ttl_ms <= 0, do: nil
def schedule_delay_ms(ttl_ms) when is_integer(ttl_ms) and ttl_ms > 0 do
delay = trunc(ttl_ms * @default_safety_margin)
min(delay, @max_auth_ttl_ms)
end
end