# `Urchin.Auth.Claims`
[🔗](https://github.com/urth-inc/urchin/blob/v0.4.0/lib/urchin/auth/claims.ex#L1)

Normalized claims for a validated OAuth 2.1 access token.

An `Urchin.Auth.Authorizer` returns one of these from `authorize/3`. The transport
surfaces it to handlers as `ctx.auth` (see `Urchin.Context`), so a handler can make
per-tool authorization decisions:

    def call_tool("delete", _args, ctx) do
      if Urchin.Auth.Claims.has_scope?(ctx.auth, "files:write") do
        # ...
      else
        {:error, "files:write scope required"}
      end
    end

`from_map/1` converts a decoded JWT payload or an RFC 7662 introspection response into
this struct, normalizing the OAuth/JWT field names (`sub`, `aud`, `scope`, `exp`, ...).

# `t`

```elixir
@type t() :: %Urchin.Auth.Claims{
  audience: [String.t()],
  claims: map(),
  client_id: String.t() | nil,
  expires_at: integer() | nil,
  scopes: [String.t()],
  subject: String.t() | nil
}
```

# `covers_resource?`

```elixir
@spec covers_resource?(t() | nil, String.t() | URI.t()) :: boolean()
```

Returns true when any token audience covers the configured resource URI.

The comparison follows the resource binding semantics Urchin used before the
authorizer handoff: the audience must share the same scheme, host and effective port,
and its path may be the resource path itself or a parent path. For example, an audience
of `https://mcp.example.com` covers `https://mcp.example.com/mcp`, but
`https://mcp.example.com/other` does not.

# `from_map`

```elixir
@spec from_map(map()) :: t()
```

Builds a `Claims` struct from a decoded token payload (string- or atom-keyed map).

Recognized fields: `sub`, `client_id`/`azp`, `exp`, `aud` (string or list),
`scope` (space-delimited string) and/or `scp`/`scopes` (string or list). JWT and RFC 7662
payloads are string-keyed, but an authorizer that hand-builds an atom-keyed map is also
accepted. The full payload is preserved under `:claims` for custom checks.

# `has_scope?`

```elixir
@spec has_scope?(t() | nil, String.t()) :: boolean()
```

Returns true when the claims grant the given scope.

# `has_scopes?`

```elixir
@spec has_scopes?(t() | nil, [String.t()]) :: boolean()
```

Returns true when the claims grant every scope in `required`.

---

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