# `AttestoPhoenix.AuthorizationServer.JwtBearer`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/authorization_server/jwt_bearer.ex#L1)

The resource server's half of the Identity Assertion JWT Authorization Grant
(ID-JAG), the grant behind MCP Enterprise-Managed Authorization (EMA) -
`draft-ietf-oauth-identity-assertion-authz-grant-04`.

A token request arrives with
`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer` and an `assertion`
parameter carrying an ID-JAG: a short-lived JWT the enterprise IdP signed
(after its own RFC 8693 token exchange) asserting one user for this resource
application. `authorize/3` turns that assertion into the local subject and
scope ceiling the token endpoint mints from. It owns the *stateful* concerns
that `Attesto.IdentityAssertion` (conn-free, pure) deliberately leaves out:

  * **issuer trust** - the assertion's `iss` must be a configured trusted
    issuer (`jwt_bearer: [issuers: %{...}]`); an unconfigured issuer is denied
    without revealing which issuers are trusted.
  * **JWKS resolution** - static keys, a cached `jwks_uri` fetch (reusing the
    SSRF-guarded CIMD fetcher + cache), or a custom `:jwks_resolver`.
  * **`jti` replay** - via the configured `:replay_check` (the same seam DPoP
    uses), namespaced so an ID-JAG `jti` never collides with a DPoP proof's.
  * **subject resolution** - the host's `:resolve_jwt_bearer_subject` callback
    maps the validated claims to a local principal subject (or denies).

Every failure returns `{:error, atom}`; the token core
(`AttestoPhoenix.AuthorizationServer.Token`) maps a missing `assertion`
parameter to RFC 6749 §5.2 `invalid_request` and every assertion/trust/replay/
subject failure to `invalid_grant`, as the draft requires.

This is NOT `private_key_jwt` client authentication (RFC 7523 §3) nor the
RFC 8693 token-exchange grant (which runs at the IdP).

# `error`

```elixir
@type error() ::
  :missing_assertion
  | :untrusted_issuer
  | :jwks_unavailable
  | :invalid_assertion
  | :replay
  | :subject_denied
```

# `result`

```elixir
@type result() :: %{
  subject: String.t(),
  scope_ceiling: [String.t()] | nil,
  claims: Attesto.IdentityAssertion.claims()
}
```

The resolved local subject, the assertion's scope ceiling (`nil` when the
assertion carried no `scope` claim, so the host policy alone decides), and the
validated claims.

# `authorize`

```elixir
@spec authorize(AttestoPhoenix.Config.t(), String.t() | nil, map()) ::
  {:ok, result()} | {:error, error()}
```

Validate the ID-JAG `assertion` and resolve the local subject.

`client_id` is the already-authenticated client's identifier (the token
endpoint resolved it from client authentication); the assertion's `client_id`
claim MUST equal it. Returns `{:ok, %{subject, scope_ceiling, claims}}` or
`{:error, t:error/0}`.

---

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