Custom Device Flow Authorization Service Implementation Specification
Applicable scenario: Enterprise users who want to control the CLI authorization process by building a private authorization service and modifying the endpoint on the client side to implement custom authorization.
1. Overview
CloudBase CLI uses the RFC 8628 — OAuth 2.0 Device Authorization Grant protocol for device authorization. The entire flow involves three endpoints:
| Endpoint | Method | Purpose | Specification Requirement |
|---|---|---|---|
/device/code | POST | Client initiates an authorization request to obtain device_code and user_code | Must comply |
/device/verify | POST | User confirms authorization in the browser | Self-implemented, reference example only |
/token | POST | Client polls to obtain authorization credentials | Must comply |
1.1 Authorization Sequence
2. Endpoint Specifications
2.1 Request Device Authorization Code
POST /device/code
Request
| Content-Type | application/json |
|---|
Request Body:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
client_id | string | Yes | 1–128 chars | Client identifier |
Request Example:
{
"client_id": "cloudbase-cli"
}
Response
Success Response (HTTP 200):
| Field | Type | Description |
|---|---|---|
device_code | string | Device code used by the client for subsequent polling to exchange for credentials. Recommended to be at least 32 bytes of cryptographically random value |
user_code | string | User code in the format XXXX-XXXX (uppercase letters + digits, excluding easily confused characters 0, 1, I, O). Users enter this in the browser to confirm authorization. The server should ensure active user_code uniqueness and detect collisions during generation |
verification_uri | string | URL of the page where users confirm authorization |
expires_in | number | Device code validity period (seconds), recommended value: 600 |
interval | number | Client polling interval (seconds), recommended value: 3 |
Response Example:
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"user_code": "QWER-ASDF",
"verification_uri": "https://example.com/cli-auth",
"expires_in": 600,
"interval": 3
}
Error Response
| Field | Type | Description |
|---|---|---|
error | string | Error code |
error_description | string | Error description |
Possible error codes:
| Error Code | Description |
|---|---|
invalid_client | client_id format is invalid or not in the allow list |
2.2 User Authorization Confirmation (Reference Example, Not Mandatory)
ℹ️ This endpoint is called by the user on the browser-side authorization page. Since users will build their own authorization page (i.e., the page pointed to by
verification_uri), the endpoint path, request parameters, and authentication method are entirely designed by the implementer. This section only provides a reference example to help understand the overall flow.
Core Responsibilities
Regardless of how the endpoint is designed, this step needs to accomplish the following:
- Verify user identity — Confirm the current operator has passed identity authentication
- Receive
user_code— User enters the user code obtained from the CLI on the authorization page - Associate authorization information — Update the
device_codestatus corresponding to theuser_codefrompendingtoauthorized, and associate the current user's identity information
Reference Example
POST /device/verify
Request Body:
{
"user_code": "QWER-ASDF"
}
Success Response:
{
"status": "USER_VERIFIED"
}
💡 Implementers can freely extend this endpoint based on business requirements, such as adding secondary confirmation, permission scope selection, multi-factor authentication, etc.
2.3 Poll for Credentials
POST /token
Request
| Content-Type | application/json |
|---|
Request Body:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
grant_type | string | Yes | Fixed value | Must be urn:ietf:params:oauth:grant-type:device_code |
device_code | string | Yes | Max 256 chars | Device code obtained from /device/code |
client_id | string | Yes | 1–128 chars | Client identifier, must match the one used when requesting the device code |
device_info | object | Yes | — | Device information object |
device_info.os | string | Yes | Max 64 chars | Operating system identifier |
device_info.mac | string | Yes | Max 128 chars | Device MAC address |
device_info.hash | string | Yes | Max 256 chars | Device unique identifier hash |
Request Example:
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"client_id": "cloudbase-cli",
"device_info": {
"os": "darwin",
"mac": "AA:BB:CC:DD:EE:FF",
"hash": "a1b2c3d4e5f6..."
}
}
Response
Authorization Success (HTTP 200)
⚠️ Differences from RFC 8628 Standard: The standard OAuth 2.0 Token Response uses fields such as
access_token,token_type,refresh_token,expires_in, andscope. The success response of this endpoint uses a CloudBase business-extended format, directly returning business fields such asrefreshTokenand temporary keys to accommodate the CloudBase CLI credential system. Implementers should return fields as specified in the table below, rather than the standard OAuth Token Response.
ℹ️ Custom Authorization Service Note: In enterprise custom authorization service scenarios, CloudBase CLI primarily relies on temporary keys for subsequent operations.
refreshTokenandexpiredare compatibility-reserved fields that typically depend on a centralized account system for generation. Enterprise custom services do not need to implement their real semantics for now.
When the user has confirmed authorization and credential generation succeeds, the following fields are returned:
| Field | Type | Description |
|---|---|---|
refreshToken | string | Compatibility-reserved field. Enterprise custom authorization services do not need to implement this; return an empty string "" |
uin | string | Authorized user's primary account UIN |
mac | string | Echo back device MAC address |
os | string | Echo back operating system identifier |
tokenId | string | Token ID, used to identify and manage login sessions |
expired | number | Compatibility-reserved field. Enterprise custom authorization services do not need to implement real expiration semantics; recommend returning 0 |
tmpToken | string | Temporary security token (STS Token) |
tmpSecretId | string | Temporary key SecretId |
tmpSecretKey | string | Temporary key SecretKey |
tmpExpired | number | Temporary key expiration timestamp (milliseconds) |
Success Response Example:
{
"refreshToken": "",
"uin": "100000001",
"mac": "AA:BB:CC:DD:EE:FF",
"os": "darwin",
"tokenId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"expired": 0,
"tmpToken": "xxxxxxxxxxxxxxxx",
"tmpSecretId": "AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tmpSecretKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tmpExpired": 1750557600000
}
💡 In custom authorization service scenarios, an empty
refreshTokenis expected behavior. The CLI completes subsequent access through temporary keys and does not require a renewable long-term token.
Authorization Incomplete / Error (HTTP 400)
When the authorization flow is not yet complete or an error occurs, HTTP 400 is returned with an error object:
| Field | Type | Description |
|---|---|---|
error | string | Error code |
error_description | string | Error description |
Error Code Reference:
| Error Code | Description | Client Behavior |
|---|---|---|
authorization_pending | User has not completed authorization | Continue polling, retry at interval intervals |
slow_down | Polling too frequently | Increase polling interval then continue polling |
expired_token | Device code has expired | Stop polling, need to re-initiate /device/code |
invalid_client | client_id mismatch | Stop polling, check client configuration |
invalid_grant | Authorization data abnormal | Stop polling, need to re-initiate the flow |
unsupported_grant_type | Unsupported grant_type | Stop polling, check request parameters |
already_consumed | Device code has been used | Stop polling, prevent replay |
Error Response Example:
{
"error": "authorization_pending",
"error_description": "The authorization request is still pending"
}
3. Device Code State Machine
The device code goes through the following state transitions during its lifecycle:
| State | Value | Description |
|---|---|---|
| Pending | pending | Initial state, waiting for user confirmation |
| Authorized | authorized | User confirmed, waiting for client to exchange credentials |
| Expired | expired | Device code has exceeded its validity period |
| Consumed | consumed | Credentials have been issued, device code cannot be reused |
4. Implementation Requirements
4.1 Security Requirements
| Requirement | Description |
|---|---|
| Transport Security | All endpoints must be served over HTTPS (TLS 1.2+); plaintext HTTP transmission is prohibited |
| Device Code Randomness | device_code should be generated using cryptographically secure random numbers, recommended at least 32 bytes |
| User Code Character Set | Use character set 23456789ABCDEFGHJKLMNPQRSTUVWXYZ (excluding 0, 1, I, O) to reduce user input errors |
| One-Time Consumption | Each device_code can only successfully exchange credentials once; it should be marked as consumed immediately after exchange |
| Replay Prevention | The consumed state should be retained for a period of time (recommended ≥ 60 seconds) to prevent replay attacks |
| Concurrency Safety | Concurrent token requests for the same device_code must be mutually exclusive to avoid duplicate credential issuance |
| Verification Endpoint Auth | /device/verify must be called in an authenticated context to ensure the operator's identity is trustworthy |
4.2 Rate Limiting Recommendations
| Endpoint | Recommended Limit |
|---|---|
/device/code | No more than 20 requests per minute per IP |
/token | No more than 120 requests per minute per IP |
/device/verify | Customize based on business requirements |
4.3 Time Parameter Recommendations
| Parameter | Recommended Value | Description |
|---|---|---|
| Device code validity period | 600 seconds (10 min) | User must complete authorization within this time |
| Polling interval | 3 seconds | Minimum interval for client polling |
| Temporary key validity period | Set based on business needs | Should cover the time needed for CLI to complete target operations |
consumed state retention | ≥ 60 seconds | Replay prevention window |
5. Client Polling Logic Reference
After obtaining the device_code, the client polls /token using the following logic:
6. Complete Flow Example
Step 1: Client Requests Device Code
curl -X POST https://your-auth-server.example.com/device/code \
-H "Content-Type: application/json" \
-d '{"client_id": "cloudbase-cli"}'
Response:
{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"user_code": "QWER-ASDF",
"verification_uri": "https://your-auth-server.example.com/cli-auth",
"expires_in": 600,
"interval": 3
}
Step 2: CLI Displays Information
Please open https://your-auth-server.example.com/cli-auth in your browser
and enter the authorization code: QWER-ASDF
Step 3: User Confirms in Browser (Designed by Implementer)
The user opens the verification_uri in the browser, completes identity authentication, and enters the user_code to confirm authorization. The following is a reference example:
curl -X POST https://your-auth-server.example.com/device/verify \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user-session-token>" \
-d '{"user_code": "QWER-ASDF"}'
Reference Response:
{
"status": "USER_VERIFIED"
}
Step 4: Client Polls for Credentials
curl -X POST https://your-auth-server.example.com/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS8A",
"client_id": "cloudbase-cli",
"device_info": {
"os": "darwin",
"mac": "AA:BB:CC:DD:EE:FF",
"hash": "a1b2c3d4e5f6..."
}
}'
Polling (user has not confirmed yet):
{
"error": "authorization_pending",
"error_description": "The authorization request is still pending"
}
Authorization Successful:
{
"refreshToken": "",
"uin": "100000001",
"mac": "AA:BB:CC:DD:EE:FF",
"os": "darwin",
"tokenId": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"expired": 0,
"tmpToken": "xxxxxxxxxxxxxxxx",
"tmpSecretId": "AKIDxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tmpSecretKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"tmpExpired": 1750557600000
}
Appendix A: Error Code Quick Reference
| Error Code | Value | Appears In |
|---|---|---|
authorization_pending | authorization_pending | /token |
expired_token | expired_token | /token |
slow_down | slow_down | /token |
invalid_client | invalid_client | /device/code, /token |
invalid_grant | invalid_grant | /token |
unsupported_grant_type | unsupported_grant_type | /token |
already_consumed | already_consumed | /token |
💡 The
/device/verifyendpoint is designed by the implementer, and its error codes are not within the scope of this specification.