# `Ankusa.Verifier.Token`
[🔗](https://github.com/jamescarr/ankusa/blob/ankusa-v0.7.0/packages/ankusa/lib/ankusa/verifier/token.ex#L1)

A static credential the provider sends with every request: a bearer token, a
basic-auth login, a token in a header of its choosing, or a token in the
query string. For providers that cannot sign (many internal systems, and
several SaaS "webhook secret" fields that are just a header value). It proves
the sender knows the secret, nothing about the body: a captured request can
be replayed and a body can be swapped. Prefer `Ankusa.Verifier.Hmac` or
`Ankusa.Verifier.Asymmetric` whenever the provider can sign.

`opts`:

  * `:secret` — the credential, or a list of them (newest first; every one
    is tried, so a rotation is `secret: [new, old]`). A missing or empty
    secret, an empty list, more than 8 secrets, or an empty element fails
    every hook with `:bad_secret`, and a source declared in config refuses to
    boot: an empty credential would accept the empty request.
  * `:location` — where the credential travels:
      * `:bearer` (default): `Authorization: Bearer <secret>`;
      * `:basic`: `Authorization: Basic base64(<secret>)`, the secret being
        `user:password` (it must contain a `:`, as every client's does);
      * `{:header, name}`: the value of header `name`, which is the secret;
      * `{:query, param}`: the value of query parameter `param`. A URL is
        logged by every proxy on the way, so this is the weakest option.

The comparison is `Ankusa.Verifier.secure_compare/2` over the SHA-256 of both
sides, so neither the content nor the length of the secret is observable by
timing, and every configured secret is compared whether or not an earlier one
matched.

The credential is *not* stored with the hook: `credential_headers/1` names the
header it travels in, and `Ankusa.Edge.Ingest` adds it to the stored-header
`drop` list for this source, so it never reaches the store, an archive
segment, a claim pack or a sink. A quarantined hook is stored without it too,
which means a quarantine replay cannot re-verify it (it fails closed with
`:missing_signature` and stays in the pen): use `on_verify_failure: reject`
for a token source.

# `location`

```elixir
@type location() :: :bearer | :basic | {:header, String.t()} | {:query, String.t()}
```

# `credential_headers`

```elixir
@spec credential_headers(keyword()) :: [String.t()]
```

The lowercase names of the request headers that carry this verifier's
credential: what the source's stored-header policy must drop. `[]` for a
location that is not a header, or opts that do not validate.

# `scheme_name`

```elixir
@spec scheme_name(keyword()) :: String.t()
```

Human name of the scheme for telemetry: `bearer`, `basic`, `header` or `query`.

# `validate_opts`

```elixir
@spec validate_opts(keyword()) :: :ok | {:error, :bad_scheme | :bad_secret}
```

Check `opts` the way `verify/2` would use them, without a request: the
location is one of the four, and every secret is a non-empty binary (with a
`:` for `:basic`).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
