# `Attesto.Config`
[🔗](https://github.com/XukuLLC/attesto/blob/v2.0.1/lib/attesto/config.ex#L1)

Immutable configuration a token operation runs against.

A `Config` binds together everything `Attesto.Token` needs that is
policy rather than protocol: who the issuer and audience are, where keys
come from, what principal kinds exist, and the default token lifetime.
Build one once (typically memoised by the host application) and pass it
to `Attesto.Token.mint/3` and `Attesto.Token.verify/3`.

## Fields

  * `:issuer` - the `iss` claim value minted into every token and
    required to match on verify. A non-empty string (an `https://` URL
    in any real deployment).
  * `:audience` - the `aud` claim value. A non-empty string.
  * `:keystore` - a module implementing `Attesto.Keystore`.
  * `:principal_kinds` - the non-empty list of `Attesto.PrincipalKind`
    structs this issuer serves. Their `claim_value`s must be distinct,
    and their `sub_prefix`es must be distinct, so the kind a token
    claims and the subject it carries map unambiguously.
  * `:principal_kind_claim` - the JWT claim name that carries the
    principal kind's `claim_value`. Defaults to `"principal_kind"`.
    A host may set its own (e.g. a namespaced private claim) without
    changing any other behaviour.
  * `:default_lifetime_seconds` - access-token lifetime when a caller
    does not request a shorter one. Defaults to `900` (15 minutes).
  * `:token_endpoint_path` - the request path the token endpoint is
    mounted at, used to derive `token_endpoint_url/1` (the URL a DPoP
    proof's `htu` must sign, and the URL OAuth metadata would publish).
    Defaults to `"/oauth/token"`.
  * `:presentation_session_store` - the verifier-side OID4VP session store,
    or `nil` when presentation sessions are not configured.
  * `:status_list_store` - the issuer-side Token Status List store, or `nil`
    when credential status tracking is not configured.

## Reserved claims

The claim names Attesto assembles itself - `iss`, `aud`, `exp`, `iat`,
`jti`, `sub`, `scope`, `typ`, `cnf`, and the configured
`principal_kind_claim` - are reserved. `Attesto.Token.mint/3` refuses a
principal whose extra claims would collide with one of them, so a caller
can never shadow a protocol claim.

# `t`

```elixir
@type t() :: %Attesto.Config{
  access_token_header_typ: String.t() | nil,
  audience: String.t(),
  c_nonce_store: module() | nil,
  credential_offer_store: module() | nil,
  default_lifetime_seconds: pos_integer(),
  issuer: String.t(),
  keystore: module(),
  pre_authorized_code_store: module() | nil,
  presentation_session_store: module() | nil,
  principal_kind_claim: String.t(),
  principal_kinds: [Attesto.PrincipalKind.t(), ...],
  status_list_store: module() | nil,
  token_endpoint_path: String.t()
}
```

# `c_nonce_store`

```elixir
@spec c_nonce_store(t()) :: module() | nil
```

Return the configured c_nonce store, if any.

# `credential_offer_store`

```elixir
@spec credential_offer_store(t()) :: module() | nil
```

Return the configured credential-offer store, if any.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Build and validate a `Config`.

    Attesto.Config.new(
      issuer: "https://api.example/",
      audience: "https://api.example/",
      keystore: MyApp.Keystore,
      principal_kinds: [
        Attesto.PrincipalKind.new("client", "oc_",
          required_claims: [{"client_id", :non_empty_string}]),
        Attesto.PrincipalKind.new("user", "usr_",
          required_claims: [
            {"act", :non_empty_string},
            {"sid", :non_empty_string},
            {"token_version", :non_neg_integer}
          ])
      ]
    )

Raises `ArgumentError` on a malformed configuration (blank issuer or
audience, a keystore that is not a module, an empty or non-list
principal-kind set, duplicate `claim_value`s or `sub_prefix`es, or a
`principal_kind_claim` that collides with a reserved claim). This is
evaluated once at boot, so it fails loudly rather than at the first
token operation.

# `pre_authorized_code_store`

```elixir
@spec pre_authorized_code_store(t()) :: module() | nil
```

Return the configured pre-authorized-code store, if any.

# `presentation_session_store`

```elixir
@spec presentation_session_store(t()) :: module() | nil
```

Return the configured presentation-session store, if any.

# `principal_kind`

```elixir
@spec principal_kind(t(), term()) :: Attesto.PrincipalKind.t() | nil
```

Return the `Attesto.PrincipalKind` whose `claim_value` equals
`claim_value`, or `nil` if no configured kind matches.

# `status_list_store`

```elixir
@spec status_list_store(t()) :: module() | nil
```

Return the configured Token Status List store, if any.

# `token_endpoint_url`

```elixir
@spec token_endpoint_url(t()) :: String.t()
```

The canonical external URL of the token endpoint: the configured issuer
merged with `token_endpoint_path`. This is the URL a client's DPoP proof
must sign in its `htu` claim (RFC 9449 §4.3) and the URL OAuth
Authorization Server Metadata (RFC 8414) would publish. Derived from
`issuer` rather than a live request so it is stable behind any TLS
terminator or reverse proxy.

---

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