How does a device with no browser redeem its device_code at the token endpoint, and with which grant_type?
answer
- back-channel POST, not a redirect
- grant_type is a URN, not a word
- the device_code goes in the body
- 400 with authorization_pending means wait
- success is an ordinary token response
basics
~10 sThe device POSTs to the token endpoint with grant_type set to urn:ietf:params:oauth:grant-type:device_code, plus its device_code and client_id. Until the user approves it gets an error response carrying authorization_pending, then a normal token response.
solid answer
~30 sRedemption is an ordinary back-channel token request, repeated. The device sends a form-encoded `POST` to the `token_endpoint` with `grant_type=urn:ietf:params:oauth:grant-type:device_code`, its `device_code` and its `client_id`. Before the user has finished, the answer is an OAuth 2.0 error response — HTTP 400 with a JSON body whose `error` is `authorization_pending`, meaning keep waiting. Once the user approves, the very same request returns the standard token response: `access_token`, `token_type`, `expires_in`, and `refresh_token` or `scope` if the authorization server issues them. Two other error codes end the loop instead: `access_denied` if the user refused, and `expired_token` once the `device_code` has outlived its `expires_in`.
code
http · 7 linesPOST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
&device_code=Cq8x9Vr2m0Tn4Lp7Wz6Jd3Hs1Kf5Bq0Xa
&client_id=s6BhdRkqt3go deeper
Remember that the device asks the token endpoint over and over with the code it was given, and that being told the request is still pending is normal rather than an error to surface.
Be able to name the three body parameters and the exact grant_type URN, and to say which replies mean keep waiting and which mean stop.
Show the failure you have actually seen: a client that branches on the HTTP status rather than the error member, and gives up while the user is still approving on their phone.
Consider the load side — thousands of devices redeeming on the same schedule after a restart, and what the token endpoint's capacity plan has to absorb.
## Where the device_code is redeemed The `device_code` is not an authorization code arriving through a browser; nothing about this grant is front-channel. The device holds the code from the moment the authorization server minted it, and redeems it by calling the **token endpoint** directly, server to server, as often as the polling rules allow. ## The request It is a form-encoded `POST` with three parameters that matter: - `grant_type` — the URN `urn:ietf:params:oauth:grant-type:device_code`. This is the single most-missed identifier on the leaf: it is a URN, not the bare string `device_code`, and it is percent-encoded inside the form body. - `device_code` — the opaque handle from the device authorization response. - `client_id` — identifying which client is asking. A client that authenticates to the token endpoint does so exactly as it would for any other grant. There is no `redirect_uri`, no `code`, and no `state`, because there was never an authorization response travelling through a browser to protect. ## The answers, one at a time Every poll gets one of four kinds of reply. Two say *keep going*, two say *stop*, and one is success: | Reply | Meaning | What the client does | |---|---|---| | token response | the user approved | stop; use the access token | | `authorization_pending` | not approved yet | wait, then poll again | | `slow_down` | still pending, and poll less often | raise the interval, then poll again | | `access_denied` | the user refused | stop and tell the user | | `expired_token` | the `device_code` is dead | stop; start a fresh device authorization request | The error replies are ordinary OAuth 2.0 error responses: HTTP 400 with a JSON body carrying `error`, and optionally `error_description` and `error_uri`. The generic token-endpoint errors still apply on top of these — `invalid_request`, `invalid_client`, `invalid_grant`, `unauthorized_client`, `unsupported_grant_type` — and none of them is a reason to keep polling. ## What success actually returns The success case is not special. It is the same JSON token response any other grant produces: `access_token`, `token_type`, `expires_in`, and `refresh_token` and `scope` where the authorization server issues them. A device that expects something bespoke here has misread the grant; the whole point of RFC 8628 is that only *acquisition* differs, and everything downstream — how the token is carried, how a resource server validates it, when it expires — is unchanged. ## The two mistakes implementations actually make 1. **Treating `authorization_pending` as a failure.** It arrives with an HTTP 400 status, which naive client code maps onto "request rejected", so the device gives up on its first poll and shows the user an error while the approval page is still open in their hand. A device-grant client has to branch on the `error` member, not on the status code alone. 2. **Polling as fast as the network allows.** The response carried an `interval` for a reason, and a client that ignores it earns `slow_down` answers that push its own wait time up. A fleet of devices restarting together and polling in lockstep is the load pattern this grant is famous for. ## What this grant does not have It is worth saying explicitly what is absent, because candidates reach for it by reflex. There is no redirect and therefore no redirect URI to register or match; there is no authorization response to bind with `state`; there is no proof key exchanged between an authorization request and a token request, because the two never travel over different channels. The `device_code` is the only thing linking the request the user approved to the token the device receives, which is exactly why it has to be high-entropy and why it is never displayed. ## Version note OAuth 2.1 removes the implicit and resource owner password credentials grants and tightens the authorization-code grant, but it does not disturb the device authorization grant: it remains the extension RFC 8628 defines, with the same URN and the same polling contract.
- What does the token endpoint return once the user has approved?The standard OAuth 2.0 token response, as JSON: `access_token`, `token_type` and `expires_in`, plus `refresh_token` and `scope` where the authorization server issues them. Nothing about the response is specific to this grant — only the way the grant was obtained differed, so the device now carries an ordinary access token and presents it to a resource server the ordinary way.
- Does a device-grant client need a registered redirect URI?No. There is no authorization response to redirect anywhere: the device never sends the user's browser to the authorization endpoint, and the `device_code` comes back on the same back-channel call that asked for it. The entire redirect apparatus — registration, exact-string matching, the callback handler — is absent from this grant, which is part of why it suits hardware with no browser.
- Why does the specification give this grant a URN instead of a short grant_type name?`grant_type` values defined outside the core framework are registered as URIs so that extensions cannot collide with each other or with the core values. `authorization_code`, `client_credentials` and `refresh_token` are core and keep bare names; every extension grant, including this one and token exchange, carries a `urn:ietf:params:oauth:grant-type:` URN instead.
saying these in an interview costs you the question
- Sends grant_type=device_code instead of the URN
- Treats the HTTP 400 on authorization_pending as a hard failure
- Expects the token to arrive on a redirect or callback
- Puts the user_code in the token request instead of the device_code
- Thinks the device must register a redirect URI first