|
| 1 | +# Device Flow CLI Example |
| 2 | + |
| 3 | +This CLI example demonstrates how to implement the **OAuth 2.0 Device Authorization Grant** (RFC 8628) flow with AIP. This flow is perfect for devices with limited input capabilities (like smart TVs, IoT devices, or CLI tools) that need to authenticate users. |
| 4 | + |
| 5 | +## 🚀 Quick Start |
| 6 | + |
| 7 | +### Prerequisites |
| 8 | + |
| 9 | +1. **AIP server running** - Make sure AIP is running on `http://localhost:8080` |
| 10 | +2. **Client management API enabled** - Set `ENABLE_CLIENT_API=true` in AIP (optional, for automatic registration) |
| 11 | + |
| 12 | +### Setup |
| 13 | + |
| 14 | +1. **Start AIP server:** |
| 15 | + ```bash |
| 16 | + # From the AIP root directory |
| 17 | + cd ../../.. |
| 18 | + ENABLE_CLIENT_API=true cargo run --bin aip |
| 19 | + ``` |
| 20 | + |
| 21 | +2. **Register the OAuth client:** |
| 22 | + ```bash |
| 23 | + # From the device-flow-cli directory |
| 24 | + ./register-client.sh |
| 25 | + ``` |
| 26 | + |
| 27 | + This will automatically register a public OAuth client with the device code grant type. |
| 28 | + |
| 29 | +### Run the Example |
| 30 | + |
| 31 | +```bash |
| 32 | +# From the device-flow-cli directory |
| 33 | +cargo run |
| 34 | + |
| 35 | +# Or with custom parameters |
| 36 | +cargo run -- --aip-url http://localhost:8080 --client-id device-flow-cli-example --scope "atproto" |
| 37 | +``` |
| 38 | + |
| 39 | +### Expected Output |
| 40 | + |
| 41 | +The CLI will guide you through the complete device flow: |
| 42 | + |
| 43 | +``` |
| 44 | +🚀 Starting OAuth 2.0 Device Authorization Grant flow |
| 45 | +📡 AIP Server: http://localhost:8080 |
| 46 | +🆔 Client ID: device-flow-cli-example |
| 47 | +
|
| 48 | +📱 Device Authorization Required |
| 49 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 50 | +📋 User Code: ABCD-1234 |
| 51 | +🌐 Verification URL: http://localhost:8080/device |
| 52 | +🔗 Quick Link: http://localhost:8080/device?user_code=ABCD-1234 |
| 53 | +⏰ Code expires in 600 seconds |
| 54 | +
|
| 55 | +🎯 Next Steps: |
| 56 | + 1. Open http://localhost:8080/device in your browser |
| 57 | + 2. Enter the user code: ABCD-1234 |
| 58 | + 3. Complete the authentication process |
| 59 | + 4. Return here - the CLI will automatically detect completion |
| 60 | +
|
| 61 | +🔄 Starting token polling (interval: 5s, timeout: 600s) |
| 62 | +📡 Polling attempt #1 |
| 63 | +⏳ Authorization still pending, waiting 5 seconds... |
| 64 | +📡 Polling attempt #2 |
| 65 | +🎉 Access token obtained successfully! |
| 66 | +
|
| 67 | +✅ Authentication Successful! |
| 68 | +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ |
| 69 | +🎫 Access Token: FdTkXbhf...yMj6aQ |
| 70 | +⏰ Expires in: 3600 seconds |
| 71 | +🏷️ Token Type: bearer |
| 72 | +📋 Granted Scope: atproto |
| 73 | +
|
| 74 | +🔍 Testing Access Token... |
| 75 | +✅ Token is valid! |
| 76 | +👤 User: did:plc:abcd1234... |
| 77 | +
|
| 78 | +🎉 Device flow complete! You can now use the access token to make authenticated API calls. |
| 79 | +``` |
| 80 | + |
| 81 | +## 🔧 Configuration |
| 82 | + |
| 83 | +### Command Line Arguments |
| 84 | + |
| 85 | +| Argument | Description | Default | |
| 86 | +|----------|-------------|---------| |
| 87 | +| `--aip-url` | AIP server base URL | `http://localhost:8080` | |
| 88 | +| `--client-id` | OAuth client ID | `device-flow-cli-example` | |
| 89 | +| `--scope` | OAuth scope (optional) | None | |
| 90 | + |
| 91 | +### Environment Variables |
| 92 | + |
| 93 | +You can also use environment variables: |
| 94 | + |
| 95 | +```bash |
| 96 | +export AIP_BASE_URL="http://localhost:8080" |
| 97 | +export CLIENT_ID="my-device-client" |
| 98 | +export OAUTH_SCOPE="atproto" |
| 99 | + |
| 100 | +cargo run |
| 101 | +``` |
| 102 | + |
| 103 | +## 🔄 How It Works |
| 104 | + |
| 105 | +The Device Authorization Grant follows these steps: |
| 106 | + |
| 107 | +### 1. **Device Authorization Request** |
| 108 | +```http |
| 109 | +POST /oauth/device/authorization |
| 110 | +Content-Type: application/x-www-form-urlencoded |
| 111 | +
|
| 112 | +client_id=device-flow-cli-example&scope=atproto |
| 113 | +``` |
| 114 | + |
| 115 | +**Response:** |
| 116 | +```json |
| 117 | +{ |
| 118 | + "device_code": "GmRhmhcxhwEzkoEqiMEg_DnyEysNkuNhszIySk9eS", |
| 119 | + "user_code": "ABCD-1234", |
| 120 | + "verification_uri": "http://localhost:8080/device", |
| 121 | + "verification_uri_complete": "http://localhost:8080/device?user_code=ABCD-1234", |
| 122 | + "expires_in": 600, |
| 123 | + "interval": 5 |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +### 2. **User Authorization** |
| 128 | +- User opens `verification_uri` in browser |
| 129 | +- Enters the `user_code` |
| 130 | +- Completes ATProtocol OAuth authentication |
| 131 | +- Authorizes the device |
| 132 | + |
| 133 | +### 3. **Token Polling** |
| 134 | +The CLI polls the token endpoint until authorization is complete: |
| 135 | + |
| 136 | +```http |
| 137 | +POST /oauth/token |
| 138 | +Content-Type: application/x-www-form-urlencoded |
| 139 | +
|
| 140 | +grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=GmRhmhcxhwEzkoEqiMEg_DnyEysNkuNhszIySk9eS&client_id=device-flow-cli-example |
| 141 | +``` |
| 142 | + |
| 143 | +**Pending Response:** |
| 144 | +```json |
| 145 | +{ |
| 146 | + "error": "authorization_pending" |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +**Success Response:** |
| 151 | +```json |
| 152 | +{ |
| 153 | + "access_token": "FdTkXbhfrQT_WS2cRFf-tX2TBWHlvPfrL-4XmyMj6aQ", |
| 154 | + "token_type": "bearer", |
| 155 | + "expires_in": 3600, |
| 156 | + "scope": "atproto" |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +### 4. **Token Usage** |
| 161 | +The CLI tests the access token by calling the session endpoint: |
| 162 | + |
| 163 | +```http |
| 164 | +GET /api/atprotocol/session |
| 165 | +Authorization: Bearer FdTkXbhfrQT_WS2cRFf-tX2TBWHlvPfrL-4XmyMj6aQ |
| 166 | +``` |
| 167 | + |
| 168 | +## 🎯 Key Features |
| 169 | + |
| 170 | +- **📱 User-friendly flow** - Clear instructions and progress indicators |
| 171 | +- **🔄 Automatic polling** - Handles token polling with proper intervals |
| 172 | +- **⚠️ Error handling** - Comprehensive error handling for all failure modes |
| 173 | +- **🎨 Rich output** - Colored output with emojis for better UX |
| 174 | +- **🔍 Token validation** - Tests the obtained token to ensure it works |
| 175 | +- **⚙️ Configurable** - Support for custom AIP URLs, client IDs, and scopes |
| 176 | + |
| 177 | +## 🛠️ Error Handling |
| 178 | + |
| 179 | +The CLI handles all standard OAuth 2.0 device flow errors: |
| 180 | + |
| 181 | +- **`authorization_pending`** - User hasn't completed authorization yet |
| 182 | +- **`slow_down`** - Polling too fast, increases interval automatically |
| 183 | +- **`expired_token`** - Device code has expired |
| 184 | +- **`access_denied`** - User denied the authorization |
| 185 | +- **Network errors** - Connection failures, timeouts, etc. |
| 186 | + |
| 187 | +## 🔐 Security Considerations |
| 188 | + |
| 189 | +- **Public client** - This example uses a public OAuth client (no client secret) |
| 190 | +- **Short-lived codes** - Device codes expire in 10 minutes by default |
| 191 | +- **Rate limiting** - Respects polling intervals and slow-down responses |
| 192 | +- **No token storage** - Tokens are only displayed, not persisted |
| 193 | + |
| 194 | +## 🏗️ Integration |
| 195 | + |
| 196 | +To integrate this pattern into your own applications: |
| 197 | + |
| 198 | +1. **Copy the core structs** - `DeviceAuthorizationRequest`, `TokenResponse`, etc. |
| 199 | +2. **Implement the three steps** - authorization request, polling, token usage |
| 200 | +3. **Handle errors gracefully** - Especially network and OAuth errors |
| 201 | +4. **Store tokens securely** - Use secure storage for production applications |
| 202 | + |
| 203 | +## 📚 References |
| 204 | + |
| 205 | +- [RFC 8628: OAuth 2.0 Device Authorization Grant](https://tools.ietf.org/html/rfc8628) |
| 206 | +- [AIP Documentation](../../../README.md) |
| 207 | +- [OAuth 2.0 Security Best Practices](https://tools.ietf.org/html/draft-ietf-oauth-security-topics) |
| 208 | + |
| 209 | +## 🐛 Troubleshooting |
| 210 | + |
| 211 | +### Client Not Found |
| 212 | +``` |
| 213 | +Device authorization request failed: 400 Bad Request - {"error":"invalid_client"} |
| 214 | +``` |
| 215 | + |
| 216 | +**Solution:** Make sure your client is registered in AIP. Use the included registration script: |
| 217 | + |
| 218 | +```bash |
| 219 | +# From the device-flow-cli directory |
| 220 | +./register-client.sh |
| 221 | +``` |
| 222 | + |
| 223 | +Or manually register using the AIP client management API. |
| 224 | + |
| 225 | +### Connection Refused |
| 226 | +``` |
| 227 | +Failed to send device authorization request: Connection refused |
| 228 | +``` |
| 229 | + |
| 230 | +**Solution:** Make sure AIP is running on the specified URL: |
| 231 | + |
| 232 | +```bash |
| 233 | +# Start AIP server |
| 234 | +cargo run --bin aip |
| 235 | +``` |
| 236 | + |
| 237 | +### Invalid Token |
| 238 | +``` |
| 239 | +❌ Token test failed: Access token test failed: 500 Internal Server Error |
| 240 | +``` |
| 241 | + |
| 242 | +**Solution:** This usually means the session is incomplete. Check AIP logs for more details. |
0 commit comments