Skip to main content

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:

EndpointMethodPurposeSpecification Requirement
/device/codePOSTClient initiates an authorization request to obtain device_code and user_codeMust comply
/device/verifyPOSTUser confirms authorization in the browserSelf-implemented, reference example only
/tokenPOSTClient polls to obtain authorization credentialsMust comply

1.1 Authorization Sequence


2. Endpoint Specifications

2.1 Request Device Authorization Code

POST /device/code

Request

Content-Typeapplication/json

Request Body:

FieldTypeRequiredConstraintsDescription
client_idstringYes1–128 charsClient identifier

Request Example:

{
"client_id": "cloudbase-cli"
}

Response

Success Response (HTTP 200):

FieldTypeDescription
device_codestringDevice code used by the client for subsequent polling to exchange for credentials. Recommended to be at least 32 bytes of cryptographically random value
user_codestringUser 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_uristringURL of the page where users confirm authorization
expires_innumberDevice code validity period (seconds), recommended value: 600
intervalnumberClient 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

FieldTypeDescription
errorstringError code
error_descriptionstringError description

Possible error codes:

Error CodeDescription
invalid_clientclient_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:

  1. Verify user identity — Confirm the current operator has passed identity authentication
  2. Receive user_code — User enters the user code obtained from the CLI on the authorization page
  3. Associate authorization information — Update the device_code status corresponding to the user_code from pending to authorized, 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-Typeapplication/json

Request Body:

FieldTypeRequiredConstraintsDescription
grant_typestringYesFixed valueMust be urn:ietf:params:oauth:grant-type:device_code
device_codestringYesMax 256 charsDevice code obtained from /device/code
client_idstringYes1–128 charsClient identifier, must match the one used when requesting the device code
device_infoobjectYesDevice information object
device_info.osstringYesMax 64 charsOperating system identifier
device_info.macstringYesMax 128 charsDevice MAC address
device_info.hashstringYesMax 256 charsDevice 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, and scope. The success response of this endpoint uses a CloudBase business-extended format, directly returning business fields such as refreshToken and 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. refreshToken and expired are 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:

FieldTypeDescription
refreshTokenstringCompatibility-reserved field. Enterprise custom authorization services do not need to implement this; return an empty string ""
uinstringAuthorized user's primary account UIN
macstringEcho back device MAC address
osstringEcho back operating system identifier
tokenIdstringToken ID, used to identify and manage login sessions
expirednumberCompatibility-reserved field. Enterprise custom authorization services do not need to implement real expiration semantics; recommend returning 0
tmpTokenstringTemporary security token (STS Token)
tmpSecretIdstringTemporary key SecretId
tmpSecretKeystringTemporary key SecretKey
tmpExpirednumberTemporary 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 refreshToken is 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:

FieldTypeDescription
errorstringError code
error_descriptionstringError description

Error Code Reference:

Error CodeDescriptionClient Behavior
authorization_pendingUser has not completed authorizationContinue polling, retry at interval intervals
slow_downPolling too frequentlyIncrease polling interval then continue polling
expired_tokenDevice code has expiredStop polling, need to re-initiate /device/code
invalid_clientclient_id mismatchStop polling, check client configuration
invalid_grantAuthorization data abnormalStop polling, need to re-initiate the flow
unsupported_grant_typeUnsupported grant_typeStop polling, check request parameters
already_consumedDevice code has been usedStop 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:

StateValueDescription
PendingpendingInitial state, waiting for user confirmation
AuthorizedauthorizedUser confirmed, waiting for client to exchange credentials
ExpiredexpiredDevice code has exceeded its validity period
ConsumedconsumedCredentials have been issued, device code cannot be reused

4. Implementation Requirements

4.1 Security Requirements

RequirementDescription
Transport SecurityAll endpoints must be served over HTTPS (TLS 1.2+); plaintext HTTP transmission is prohibited
Device Code Randomnessdevice_code should be generated using cryptographically secure random numbers, recommended at least 32 bytes
User Code Character SetUse character set 23456789ABCDEFGHJKLMNPQRSTUVWXYZ (excluding 0, 1, I, O) to reduce user input errors
One-Time ConsumptionEach device_code can only successfully exchange credentials once; it should be marked as consumed immediately after exchange
Replay PreventionThe consumed state should be retained for a period of time (recommended ≥ 60 seconds) to prevent replay attacks
Concurrency SafetyConcurrent 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

EndpointRecommended Limit
/device/codeNo more than 20 requests per minute per IP
/tokenNo more than 120 requests per minute per IP
/device/verifyCustomize based on business requirements

4.3 Time Parameter Recommendations

ParameterRecommended ValueDescription
Device code validity period600 seconds (10 min)User must complete authorization within this time
Polling interval3 secondsMinimum interval for client polling
Temporary key validity periodSet based on business needsShould cover the time needed for CLI to complete target operations
consumed state retention≥ 60 secondsReplay 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 CodeValueAppears In
authorization_pendingauthorization_pending/token
expired_tokenexpired_token/token
slow_downslow_down/token
invalid_clientinvalid_client/device/code, /token
invalid_grantinvalid_grant/token
unsupported_grant_typeunsupported_grant_type/token
already_consumedalready_consumed/token

💡 The /device/verify endpoint is designed by the implementer, and its error codes are not within the scope of this specification.