The tokens the client holds for one {principal, issuer, resource}.
access_token and refresh_token are ConduitMcp.Client.Secrets, so
inspect/1, logs and crash reports show #Secret<redacted>; the bearer
is exposed only where the Authorization header is built. expires_at is
Unix seconds computed locally from expires_in (nil when the server gave
no lifetime).
iex> tokens = ConduitMcp.Client.OAuth.Tokens.new(access_token: "at", expires_at: 1_000)
iex> inspect(tokens) =~ "at\""
false
iex> ConduitMcp.Client.OAuth.Tokens.expired?(tokens, 960, 30)
false
iex> ConduitMcp.Client.OAuth.Tokens.expired?(tokens, 970, 30)
true
Summary
Functions
Whether the access token is expired, or expires within skew_seconds, at
Unix time now: now + skew_seconds >= expires_at. Tokens without
expires_at never expire by time (a 401 still triggers a refresh).
Builds tokens from fields, wrapping :access_token and :refresh_token
in ConduitMcp.Client.Secret (values already wrapped are kept).
Types
@type t() :: %ConduitMcp.Client.OAuth.Tokens{ access_token: ConduitMcp.Client.Secret.t(), expires_at: integer() | nil, refresh_token: ConduitMcp.Client.Secret.t() | nil, scopes: [String.t()], token_type: String.t() }
Functions
@spec expired?(t(), integer(), non_neg_integer()) :: boolean()
Whether the access token is expired, or expires within skew_seconds, at
Unix time now: now + skew_seconds >= expires_at. Tokens without
expires_at never expire by time (a 401 still triggers a refresh).
Builds tokens from fields, wrapping :access_token and :refresh_token
in ConduitMcp.Client.Secret (values already wrapped are kept).
Raises ArgumentError when :access_token is missing or a field has the
wrong type, and KeyError on an unknown field.