EndPointBlank.Commands.GenerateAccessToken (end_point_blank_elixir v0.9.0)

Copy Markdown View Source

Requests a new access token from the EndPointBlank API.

Two entry points, on purpose. generate/1 is the original one and answers the payload or nil; generate_result/1 answers an {:ok, _} | {:error, _} tuple that says why a mint failed. The status intake returns carries a decision a caller cannot otherwise make -- a rejected credential is permanent until someone re-issues it, an intake that fell over is not -- and nil erases it.

Summary

Types

Why a mint failed.

Functions

Requests a new access token for base_url.

Requests a new access token for base_url, reporting why a failure failed.

Types

failure()

@type failure() ::
  :credential_rejected
  | {:request_rejected, pos_integer()}
  | {:server_error, pos_integer()}
  | {:transport_error, term()}

Why a mint failed.

Permanent -- the identical call cannot succeed until something changes outside this process:

  • :credential_rejected -- intake answered 401. The client_id / client_secret this SDK is configured with was refused, and will go on being refused until the credential is re-issued.
  • {:request_rejected, status} -- intake answered some other 4xx. What was asked for cannot be granted: intake's access-token controller sends 400 for an invalid token_ttl or a missing base_url, and 422 for a target or source application it does not know. The credential is fine; the request or the registration is not.

Transient -- worth trying again:

  • {:server_error, status} -- intake is broken. A 5xx, any other unexpected non-2xx, or a 2xx this cannot read an access token out of -- an undecodable body, a body that is not a JSON object, or one carrying no token or no base_url. The status is the real one intake sent, including when it was a 2xx; why a 2xx was unusable is in the log line rather than in the return value, which is where a human debugging it looks.
  • {:transport_error, reason} -- no HTTP status was obtained at all: connection refused, timeout, Http.post/3's three attempts exhausted. Only a real transport failure lands here (a Req.TransportError or Req.HTTPError, their Mint/Finch equivalents, or an atom such as :timeout); anything else the HTTP client hands back is {:unexpected, reason}, which generate_result/1 answers but which is not a failure/0.

Functions

generate(base_url)

Requests a new access token for base_url.

Returns the parsed response map (token, expired_at, base_url) on success, or nil on failure.

Unchanged, deliberately: this is a published library, and callers depend on the payload-or-nil contract. It is a thin wrapper over generate_result/1, which is where a caller that needs to tell a rejected credential from a failed intake should go instead.

generate_result(base_url)

@spec generate_result(term()) ::
  {:ok, map()}
  | {:error,
     failure()
     | :invalid_base_url
     | :missing_credentials
     | {:unexpected, term()}}

Requests a new access token for base_url, reporting why a failure failed.

base_url has its userinfo, query and fragment removed, its scheme and host lowercased and a default or empty port dropped (EndPointBlank.OutboundUrl.strip/1); the path is sent as written, unconditionally alongside token_ttl (which goes over the wire as an explicit null when unconfigured — intake handles that deliberately). intake normalizes base_url and matches it against registered base URLs by longest path prefix. EndPointBlank.AccessTokens strips before it gets here; this strips again so a direct caller cannot send intake a secret either.

Returns {:ok, payload} -- a map carrying a non-empty token and base_url, plus whatever else intake sent (expired_at) -- or {:error, reason} where reason is a failure/0. See that type for which reasons are permanent and which are worth retrying. A base_url that is not a string, or that OutboundUrl.strip/1 refuses (not an absolute http or https URL with a host, or a port outside 1..65535), answers {:error, :invalid_base_url} without a request.

Two answers are not intake's verdict and are not a failure/0:

  • {:error, :missing_credentials} -- client_id or client_secret is nil or empty, so nothing is sent. Sending it anyway would go out as Basic of ":" and come back as a 401 that reads as a revoked credential (sc-1469).
  • {:error, {:unexpected, reason}} -- the HTTP client answered an error that is not a transport failure (see failure/0): a bug or a bad setting, not intake being out of reach. Anything raised while minting propagates as itself; EndPointBlank.AccessTokens reports it the same way.