OAUTH 2.0 · LAB
Approve a photo frame on another screen with device authorization
Planned: start a device authorization as a photo frame, approve it on your phone, and poll for the result. Today, confirm what the tenant offers and use the loopback flow a browser-capable tool should use.
PlannedUses your lab tenant
The lesson
Builds on: Public and confidential clients.
New to the labs? Start with the lab toolkit and the shared cast and names every lab uses.
Planned. The core of this lab waits on platform features that are not built yet. The planned walkthrough shows exactly how it will run; Do today is a real exercise you can do now.
- G5 Device authorization grant
Request console
Requests in this lab can be sent from this page to your tenant: open one and choose Send. Fill in the values below first. They stay in this page's memory and are gone when you leave; secrets are never stored or sent anywhere except the request you send.
Setup
Set
ISSUERandAPP_ID(thelab-printer-appclient ID) in your shell, and defineencas in the earlier labs.
Planned walkthrough
These steps need the device authorization grant (G5). They are written exactly as they will run.
Allow the grant and register the frame. In OAuth > Flow policy, tick Device authorization. Create a temporary public client
lab-tmp-framefrom a Device preset, with the device authorization grant and no redirect URIs. Choose the tenant's user code format and length, code lifetime and minimum polling interval. SetFRAME_IDto its client ID.
The frame asks. It is a public client, so it sends only its client ID.
curl -s -d "client_id=$FRAME_ID" -d scope=photos.read "$ISSUER/oauth/device_authorization" | jq
The answer has device_code, user_code (for example WDJB-MJHT), verification_uri ($ISSUER/oauth/device), verification_uri_complete, expires_in and interval. Keep DEVICE_CODE in the shell; show only the user code and the address, as the frame's screen would.
The frame polls before anyone has approved.
curl -s -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d "device_code=$DEVICE_CODE" -d "client_id=$FRAME_ID" "$ISSUER/oauth/token" | jq
The answer is 400 authorization_pending. Poll again sooner than interval: slow_down, and the frame adds five seconds to its interval for the rest of the attempt.
Approve on another device. On your phone or in a second browser, open
verification_uri, sign in as Ava and type the user code. The approval page says a device is asking, nameslab-tmp-frameand listsphotos.read. Approve.
The next poll returns an ordinary token response with an access token for Ava.
Arrive through
verification_uri_completeon a fresh attempt. The page still shows the code and asks Ava to confirm that it matches the code on the frame's screen. With the frame's screen in front of you, compare them before approving.
Planned failure cases: choose Deny (the next poll answers
access_denied), let a code lapse (expired_token), and type a wrong user code several times (refused, then rate limited with429and a visible usage limit). Audit records each stage.
Do today
Read what the tenant advertises.
curl -s "$ISSUER/.well-known/openid-configuration" | jq '{device_authorization_endpoint, grant_types_supported, btl_service_status}'
There is no device authorization endpoint, no device grant, and btl_service_status is "partial". Flow policy lists Device authorization under "Not yet available".
Ask the catalogued endpoint anyway.
POST$ISSUER/oauth/device_authorization
Open in console
POST $ISSUER/oauth/device_authorization HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client_id=$APP_ID&scope=photos.readThe answer is 501 with {"error":"temporarily_unavailable","error_description":"This endpoint is not implemented yet."}.
Poll the token endpoint with the device grant type, as a frame would.
curl -s -d grant_type=urn:ietf:params:oauth:grant-type:device_code -d device_code=none -d "client_id=$APP_ID" "$ISSUER/oauth/token" | jq
The answer is unsupported_grant_type. The full URN is how extension grants are named; this server does not implement this one.
Use the flow the lesson recommends for a tool that can open a browser. Treat
lab-printer-appas a command-line tool: runbtl-lab callback --port 8765in a second terminal, then open the authorization request in the system browser.
eval "$(btl-lab pkce)"; eval "$(btl-lab state)"
echo "$ISSUER/oauth/authorize?response_type=code&client_id=$APP_ID&redirect_uri=$(enc http://127.0.0.1:8765/callback)&scope=photos.read&state=$STATE&code_challenge=$CHALLENGE&code_challenge_method=S256"
Sign in as Ava and approve. The listener prints the code, and the tool exchanges it with its verifier as in the Public and confidential clients lab. Approval and result stay together on one device, which the device grant cannot offer.
Check your work
Today, in tenant Logs: oauth.device rejected not_implemented with status 501, and oauth.token rejected unsupported_grant_type with status 400. Audit has the loopback flow's oauth.authorize code_issued and oauth.token succeeded for lab-printer-app.
Once G5 exists, the lab will check device code issue, user code acceptance, approval or denial, and token issue in Audit.
Cleanup
None today. The planned walkthrough deletes lab-tmp-frame and unticks Device authorization in Flow policy when it finishes.
Missing infrastructure
G5, device authorization grant. The device authorization endpoint; a tenant-hosted verification page with user code entry and the match-the-screen confirmation for
verification_uri_complete; polling answersauthorization_pending,slow_down,access_deniedandexpired_token; a Flow policy option and a per-client grant; tenant settings for user code format and length, code lifetime and polling interval; attempt limits on code entry shown as a usage limit; and Audit events for each stage. With it, every step of the planned walkthrough runs as written.