The device verification page (RFC 8628 §3.3), at {oauth_path}/device:
where a person enters the user code a CLI or a device shows them, then
approves or denies its request on the consent page. Mount it, like
Sake.Auth.Authorize, inside the pipeline that requires a signed-in
person; Sake reads the person through Sake.Auth.Subject. It answers
404 without device: in the auth config.
GET shows the code entry form (Sake.Auth.Consent.render_user_code/1),
prefilled from verification_uri_complete's user_code; nothing is
looked up until the person submits it. A posted code shows the
consent page, with the code on it to compare with the device's. A
code that is unknown, expired or already decided is refused with the
same message, and each signed-in person may enter five codes a minute,
which is what keeps the eight characters of a user code out of reach
of guessing. The consent page shows even when the person approved the
client before: whoever started the request may have sent the code to
someone else to approve.
Both forms carry a signed blob bound to the signed-in person, which
expires after ten minutes, and the host's CSRF token when its pipeline
runs Plug.CSRFProtection, as Sake.Auth.Authorize's form does. The
pages refuse to be framed.
A device request may say how the person must have signed in (RFC
9470: acr_values, max_age). Once the person submits its code, a
sign-in that does not meet it (by the subject module's
authentication/1) is sent to reauthenticate/2, with the conn's
query string set to the code (user_code=...), so that coming back to
request_path?query_string shows the form with the code filled in;
the grant stays pending meanwhile. The approval records the acr and
auth_time of the person's sign-in, which the tokens carry.