# `AttestoPhoenix.Schema.ClientIdMetadata`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/schema/client_id_metadata.ex#L1)

Ecto schema for one cached Client ID Metadata Document - CIMD
(`draft-ietf-oauth-client-id-metadata-document-01`, IETF OAuth WG).

CIMD lets a client identify itself with no prior registration by using an
HTTPS URL as its `client_id`; the authorization server dereferences that URL
and validates the returned document. The default cache,
`AttestoPhoenix.ClientIdMetadata.Cache.Ecto`, persists each *successfully
validated* document so the cache is coherent across a cluster (a document
fetched on one node is served from every node) and the outbound fetch
fan-out is bounded under load. This schema backs that store, one row per
cached `client_id` URL.

Per the draft (§6) and RFC 9111, only a validated document is ever written
here - error responses and malformed documents are never cached - so a row's
presence means the document was accepted at fetch time. Freshness is still
re-checked on read against `expires_at`, so an unswept expired row is treated
as a miss and never served.

## Columns

  * `url` - the CIMD `client_id` URL the document was fetched from (and which
    the document's own `client_id` equals). It is the PRIMARY KEY, so the
    cache lookup (`get/1`) hits the primary key directly and a re-fetch of the
    same URL upserts the single row rather than accumulating duplicates.
  * `metadata` - the validated, string-keyed client metadata map (the RFC 7591
    Dynamic Client Registration field set the AS uses as the client).
    Persisted as `jsonb`; the cache reads back the same string-keyed map it
    stored.
  * `expires_at` - when the cached document stops being fresh, derived from the
    response's `Cache-Control: max-age` / `Expires` (RFC 9111) clamped to the
    host's configured bounds. The store rejects an expired row on read, so an
    unswept expired entry is never honored.
  * `inserted_at` - when the document was cached (diagnostic; never a lookup
    key).

# `t`

```elixir
@type t() :: %AttestoPhoenix.Schema.ClientIdMetadata{
  __meta__: term(),
  expires_at: DateTime.t() | nil,
  inserted_at: DateTime.t() | nil,
  metadata: map() | nil,
  url: String.t() | nil
}
```

A persisted cached Client ID Metadata Document row.

# `put_changeset`

```elixir
@spec put_changeset(
  t()
  | %AttestoPhoenix.Schema.ClientIdMetadata{
      __meta__: term(),
      expires_at: term(),
      inserted_at: term(),
      metadata: term(),
      url: term()
    },
  map()
) :: Ecto.Changeset.t()
```

Changeset for caching a freshly fetched, validated metadata document.

Requires the `url` key, the validated `metadata`, and both instants. A cached
document with no expiry would never fail closed on read, so a missing
`:expires_at` is a hard validation error rather than a silently unlimited
cache entry.

---

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