# TVs, CLIs and devices

> Sign users in on devices without a keyboard or browser with the device authorization grant (RFC 8628).

Source: https://9in.io/docs/devices/

Smart TVs, consoles, CLIs and other devices without a good keyboard or browser use the **device authorization grant** (RFC 8628): the device shows a short code, the user enters it on their phone or computer, and the device receives tokens once they approve.

## Set up the application

**Applications → Create application → TV & Device**. It's a public client (no secret) with the device code and refresh token grants.

## 1. Ask for a code

```bash
curl -X POST https://acme.9in.io/oauth/device/authorize \
  -d client_id=app_tv123 \
  -d scope="openid profile offline_access"
  # -d audience=https://api.acme.com   tokens for your API
```

```json
{
  "device_code": "Ag5m…",
  "user_code": "BDWP-HQXZ",
  "verification_uri": "https://acme.9in.io/device",
  "verification_uri_complete": "https://acme.9in.io/device?user_code=BDWP-HQXZ",
  "expires_in": 600,
  "interval": 5
}
```

Show the user `verification_uri` and `user_code` (or a QR code of `verification_uri_complete`, which fills the code in). Codes are 8 letters without vowels, so they never spell words, and expire after 10 minutes.

## 2. Poll for tokens

Every `interval` seconds:

```bash
curl -X POST https://acme.9in.io/oauth/token \
  -d grant_type=urn:ietf:params:oauth:grant-type:device_code \
  -d device_code=Ag5m… \
  -d client_id=app_tv123
```

| Response | Meaning | What to do |
|---|---|---|
| `400 authorization_pending` | The user hasn't finished yet | Keep polling |
| `400 slow_down` | You polled too fast | Add 5 seconds to the interval |
| `400 access_denied` | The user declined | Stop; offer to start over |
| `400 expired_token` | 10 minutes passed | Start over with a new code |
| `200` with tokens | Approved | Store the tokens and stop polling |

## 3. Use the tokens

The response is the same as any sign-in: an access token, an ID token with `openid`, and a refresh token with `offline_access`. Refresh with `grant_type=refresh_token` and your `client_id`; refresh tokens rotate, so store the new one each time.

## What the user sees

At `verification_uri` the user signs in (if they aren't already), types the code, sees which app is asking and for what, and approves. The page is themed and translated like every other hosted page.
