TVs, consoles and command-line tools
A TV can’t easily show a sign-in page, and a command-line tool shouldn’t ask for a password. With the device flow, the device shows a short code, and the person types it into their environment’s Lobby on their phone or computer, where they are already signed in.
Register the app
In Usher Console, open the environment, then Applications → New application → TV or command line. It gets a client ID and no secret: a device can’t keep one.
The flow
# 1. the device asks for a code
curl -s -X POST https://acme.id.hallelo.net/api/auth/device/code \
-d client_id=YOUR_CLIENT_ID -d scope='openid email offline_access'
# → { "device_code": "…", "user_code": "6L95PBQQ",
# "verification_uri": "https://acme.id.hallelo.net/device",
# "verification_uri_complete": "https://acme.id.hallelo.net/device?user_code=6L95PBQQ",
# "expires_in": 600, "interval": 5 }
# 2. it shows the code (and a QR code of verification_uri_complete), then polls
curl -s -X POST https://acme.id.hallelo.net/api/auth/oauth2/token \
-d grant_type=urn:ietf:params:oauth:grant-type:device_code \
-d device_code=… -d client_id=YOUR_CLIENT_ID
# → { "error": "authorization_pending" } until the person allows it,
# then { "access_token": "…", "id_token": "…", "refresh_token": "…" }- Poll politely. Wait
intervalseconds between polls. Polling faster is answeredslow_down. - Stop when told.
access_deniedmeans the person refused;expired_tokenmeans ten minutes passed. Ask for a new code. - Show the address too. Not everyone can scan a QR code: show
verification_uriand the code, spaced like6L95-PBQQ.
At /device, the Lobby shows the person which app is asking and for what. It tells them to go on only if they started it themselves and their device shows the same code.
Bind tokens to the device (DPoP)
A token copied off a device is still a token. With DPoP (RFC 9449), the device keeps a private key and sends a fresh signed proof with every request. A token bound to that key is useless without it.
- Ask for bound tokens: send a
DPoPheader with your token requests, and Usher binds what it issues to your key (token_typeisDPoP). - Require them: turn on Require DPoP when you create the app. Usher then issues it no token without a proof.
- Use them with
Authorization: DPoP <token>, plus a proof for that request. - Libraries: most OAuth libraries with DPoP support do all of this for you (
openid-client’sDPoPoption, for example).