Stevedore.Auth (Stevedore v0.1.0)

Copy Markdown View Source

Registry credentials and the bearer-token challenge/exchange flow.

Registries advertise how to authenticate via a 401 response carrying a WWW-Authenticate: Bearer realm=…,service=…,scope=… header. parse_challenge/1 reads that header; token/3 exchanges it (plus optional credentials) for a bearer token at the realm's token endpoint. Credentials can be supplied directly or loaded from ~/.docker/config.json.

Spec: Docker token authentication and the distribution-spec authentication section.

Summary

Functions

Loads credentials from a Docker config file (default ~/.docker/config.json), returning a map of registry host to creds/0. A missing file yields an empty map.

Parses a WWW-Authenticate header value. Only the Bearer scheme is supported; Basic and unknown schemes return {:error, :unsupported}.

Exchanges a parsed challenge for a bearer token, sending creds as HTTP Basic to the token endpoint when they are not :anonymous.

Types

challenge()

@type challenge() :: %{
  realm: String.t(),
  service: String.t() | nil,
  scope: String.t() | nil
}

creds()

@type creds() :: :anonymous | {:basic, user :: String.t(), pass :: String.t()}

Functions

from_docker_config(path \\ nil)

@spec from_docker_config(Path.t() | nil) ::
  {:ok, %{optional(String.t()) => creds()}} | {:error, term()}

Loads credentials from a Docker config file (default ~/.docker/config.json), returning a map of registry host to creds/0. A missing file yields an empty map.

parse_challenge(header)

@spec parse_challenge(String.t()) :: {:ok, challenge()} | {:error, :unsupported}

Parses a WWW-Authenticate header value. Only the Bearer scheme is supported; Basic and unknown schemes return {:error, :unsupported}.

Examples

iex> Stevedore.Auth.parse_challenge(~s(Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/alpine:pull"))
{:ok, %{realm: "https://auth.docker.io/token", service: "registry.docker.io", scope: "repository:library/alpine:pull"}}

iex> Stevedore.Auth.parse_challenge("Basic realm=\"registry\"")
{:error, :unsupported}

token(challenge, creds \\ :anonymous, opts \\ [])

@spec token(challenge(), creds(), keyword()) ::
  {:ok, String.t()} | {:error, Stevedore.Auth.Error.t()}

Exchanges a parsed challenge for a bearer token, sending creds as HTTP Basic to the token endpoint when they are not :anonymous.

opts may carry :req_options (a keyword merged into the Req request, e.g. an :adapter) for testing.