The device flow
Sign people in from command-line tools and devices that cannot receive a callback
In the examples, api.<domain> is your Ghost API host and <web> is the Ghost web app.
Command-line tools and devices that cannot receive a callback use the device flow, as gh auth login --web does. Turn on Enable device flow in the app's settings first; Ghost refuses device codes for apps without it.
-
Ask for a device code:
curl -X POST https://api.<domain>/login/device/code \ -H "Accept: application/json" \ -d client_id=DEyiWXBFvHvUTGkCIQzDrbNEuetuxVOr \ -d scope=repo{ "device_code": "XUNcGWbIlQkErNKuRnc2jOwijqRBr203qvVoNZCx", "user_code": "4TGNAEHZ", "verification_uri": "https://<web>/device", "expires_in": 1800, "interval": 5 } -
Show the person the
user_codeand theverification_uri. They open it, enter the code, and approve:
-
Meanwhile, poll for the token every
intervalseconds:curl -X POST https://api.<domain>/login/oauth/access_token \ -H "Accept: application/json" \ -d client_id=DEyiWXBFvHvUTGkCIQzDrbNEuetuxVOr \ -d device_code=XUNcGWbIlQkErNKuRnc2jOwijqRBr203qvVoNZCx \ -d grant_type=urn:ietf:params:oauth:grant-type:device_codeUntil the person decides, Ghost answers HTTP 200 with
{"error": "authorization_pending", "error_description": "…"}. Keep polling. Onslow_down, wait longer between polls.expired_tokenmeans the code ran out;access_deniedmeans the person said no. Once they approve, the answer is a token, in the same shape as the code exchange.
The device flow needs no client secret, so a tool can ship its client ID in its code.