# `AttestoPhoenix.Controller.UserinfoController`
[🔗](https://github.com/XukuLLC/attesto_phoenix/blob/v0.19.0/lib/attesto_phoenix/controller/userinfo_controller.ex#L1)

OpenID Connect UserInfo endpoint (OpenID Connect Core 1.0 §5.3).

Returns claims about the authenticated subject as a JSON object. The
endpoint is a protected resource: the caller presents the access token issued
during authentication, and the endpoint releases the subject claims the
token's scopes authorize.

## Authentication

Verification is delegated to the engine's protected-resource verify path,
`Attesto.Plug.Authenticate`, which this controller runs at the top of its
action. That plug parses the `Authorization` header (Bearer, RFC 6750 §2.1,
or DPoP, RFC 9449 §7.1), verifies the access token through `Attesto.Token`,
and - for a sender-constrained token - enforces the DPoP / mTLS binding,
honouring `cnf.jkt` / `cnf.x5t#S256`. A DPoP-bound token presented under the
Bearer scheme is rejected there, not here. On failure the plug halts the conn
with the RFC 6750 §3 / RFC 9449 §7.1 `WWW-Authenticate` challenge, which this
controller returns unchanged.

Per OpenID Connect Core §5.3.1 both `GET` and `POST` are accepted; the host
router maps both verbs to the `:userinfo` action.

## Authorization

The verified access token MUST carry the `openid` scope (OpenID Connect Core
§5.3.1). A token without it is answered `403` with `error="insufficient_scope"`
and the `scope="openid"` auth-param (RFC 6750 §3.1).

## Claims

The scopes on the access token (its `scope` claim, RFC 9068 §2.2.3) gate which
claims are released (OpenID Connect Core §5.4):

  * `profile` - the OpenID Connect Core §5.4 profile claim set.
  * `email` - `email` and `email_verified`.
  * `address` - the `address` claim (a JSON object, OpenID Connect Core §5.1.1).
  * `phone` - `phone_number` and `phone_number_verified`.

The host supplies the claim *values* through the `:build_userinfo_claims`
callback (see `AttestoPhoenix.Config`); this controller keeps only the values
the granted scopes authorize and always includes `sub` (OpenID Connect Core
§5.3.2), the stable subject identifier, regardless of scope.

Beyond the scope-implied set, individual claims requested through the OpenID
Connect `claims` request parameter's `userinfo` member (OpenID Connect Core
§5.5) are also released. The authorization endpoint records that parameter on
the access token (its `claims` claim) at issuance; the verify path surfaces it
here, and the named claims are added to the release allow-list so a Relying
Party can obtain a single claim without requesting the whole scope. A claim
the host's source does not supply is simply omitted (a UserInfo response need
not contain every requested claim, OpenID Connect Core §5.5). When the
provider advertises `claims_parameter_supported: false` (the default, see
`AttestoPhoenix.Config`), the access token carries no `claims` claim and this
reduces to scope-gated release.

## Configuration contract

Resolved through `AttestoPhoenix.Config` (see that module for the
authoritative definitions):

  * `:build_userinfo_claims` - the host's claim source (required to mount
    this endpoint).
  * `:issuer`, `:audience`, `:keystore`, `:access_token_ttl` - claim-level
    policy supplied to the engine verify path as an `Attesto.Config`.
  * `:dpop_enabled`, `:dpop_nonce_required`, `:nonce_store`, `:replay_check`,
    `:cert_der`, `:mtls_enabled`, `:htu` - sender-constraint policy and
    stores, threaded into `Attesto.Plug.Authenticate`.

# `userinfo`

```elixir
@spec userinfo(Plug.Conn.t(), map()) :: Plug.Conn.t()
```

UserInfo action (OpenID Connect Core §5.3). Handles both `GET` and `POST`
(OpenID Connect Core §5.3.1).

Named `userinfo` rather than `call` so it does not collide with the
`Phoenix.Controller` plug entrypoint (`Phoenix.Controller.Pipeline.call/2`)
that dispatches the action.

---

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