# `Attesto.PrincipalKind`
[🔗](https://github.com/XukuLLC/attesto/blob/v0.13.0/lib/attesto/principal_kind.ex#L1)

One kind of subject a token can describe.

A single issuer and verifier can serve several kinds of principal - a
machine client authenticating with `client_credentials`, a human whose
dashboard session was exchanged for a token, a device. They share every
standard claim (`iss`, `aud`, `exp`, `sub`, `scope`, …) and differ only
in three policy-defined ways, which this struct captures:

  * **`claim_value`** - the value carried in the configured principal-kind
    claim (see `Attesto.Config`). A token's kind is read from this claim
    and cross-checked, so a token can never be silently routed down the
    wrong principal path.

  * **`sub_prefix`** - the namespace prefix the `sub` claim MUST carry
    for this kind (e.g. `"oc_"` for a client, `"usr_"` for a user). The
    verifier requires `sub` to start with the prefix of the kind named
    in the principal-kind claim; a mismatch fails verification. This is
    defense-in-depth against a token whose kind claim and subject
    disagree.

  * **`required_claims`** - extra claims this kind MUST carry, each with
    a shape. A client carries `client_id`; a user-session token might
    carry an acting-account id, a session id, and a token-version
    counter for bulk revocation. The verifier (and the minter) reject a
    token of this kind that is missing one, or whose value has the wrong
    shape, so downstream code never branches on a `nil` it assumed was
    present.

Attesto does not interpret what a kind *means* - it only enforces the
cross-checks. The meaning is the host application's policy.

## Required-claim shapes

Each entry in `required_claims` is `{claim_name, shape}` where `shape`
is one of:

  * `:non_empty_string` - a binary that is not `""`.
  * `:string` - any binary (may be empty).
  * `:non_neg_integer` - an integer `>= 0`.

# `shape`

```elixir
@type shape() :: :non_empty_string | :string | :non_neg_integer
```

# `t`

```elixir
@type t() :: %Attesto.PrincipalKind{
  claim_value: String.t(),
  required_claims: [{String.t(), shape()}],
  sub_prefix: String.t()
}
```

# `check_required`

```elixir
@spec check_required(t(), %{optional(String.t()) =&gt; term()}) ::
  :ok | {:error, {String.t(), :missing | :wrong_shape}}
```

Returns `:ok` if `claims` carries every required claim for this kind
with the correct shape, or `{:error, {claim_name, :missing | :wrong_shape}}`
on the first violation.

# `new`

```elixir
@spec new(String.t(), String.t(), keyword()) :: t()
```

Build a principal kind.

    Attesto.PrincipalKind.new("client", "oc_",
      required_claims: [{"client_id", :non_empty_string}]
    )

Raises `ArgumentError` if `claim_value`/`sub_prefix` are not non-empty
binaries or any required-claim shape is unknown - this is configuration,
evaluated once at boot, so a malformed kind should fail loudly rather
than at the first token operation.

---

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