OAuth device authorization

OAuth 2.0 Device Authorization Grant (RFC 8628) lets applications running in environments without browser access (such as SSH sessions, remote development environments, or constrained devices) authenticate with Buildkite. The device requests a code and polls for a token, while the user completes authorization in a browser on any device.

How it works

┌────────────┐  1. POST /oauth/device_authorization   ┌──────────────┐
│            │ ──────────────────────────────────────▶│              │
│   Device   │ ◀──────────────────────────────────────│  Buildkite   │
│            │    device_code + user_code             │              │
│            │                                        └──────────────┘
│            │  2. Display user_code to user
│            │     User visits verification_uri       ┌──────────────┐
│            │     and enters user_code              ▶│   Browser    │
│            │                                        │ (any device) │
│            │                                        └──────────────┘
│            │  3. Poll POST /oauth/token             ┌──────────────┐
│            │ ──────────────────────────────────────▶│              │
│            │ ◀──────────────────────────────────────│  Buildkite   │
└────────────┘   access_token (once approved)         └──────────────┘
  1. Request a device code from POST /oauth/device_authorization.
  2. Display the user_code to the user and direct them to the verification_uri to enter the code and authorize the application in a browser.
  3. Poll POST /oauth/token with the device_code at the specified interval until the user approves, denies, or the code expires.

Device authorization request

Request a device code by sending a POST request to the device authorization endpoint:

Endpoint: POST https://buildkite.com/oauth/device_authorization

POST /oauth/device_authorization HTTP/1.1
Host: buildkite.com
Content-Type: application/x-www-form-urlencoded

client_id=your-client-id
&scope=read_user read_organizations

Request parameters

Parameter Required Description
client_id Yes The client ID of your OAuth application
scope Yes Space-delimited list of scopes. At least one valid scope is required
client_secret Conditional Required for confidential clients. Not required for public clients

Response

A successful response returns a JSON object:

{
  "device_code": "GmRhmhcxfnZfIQFAcZx7VFMylYeXPOO5",
  "user_code": "BCDF-GHJK",
  "verification_uri": "https://buildkite.com/oauth/device",
  "verification_uri_complete": "https://buildkite.com/oauth/device/BCDF-GHJK",
  "expires_in": 600,
  "interval": 5
}
Field Description
device_code The device verification code used to poll the token endpoint
user_code The user-facing code displayed to the user, formatted as XXXX-XXXX
verification_uri The URL where the user enters the code
verification_uri_complete The full verification URL with the code pre-filled
expires_in Seconds until the codes expire (600 seconds, that is, 10 minutes)
interval Minimum seconds between polling requests (starts at 5 seconds)

User authorization

Direct the user to the verification_uri (https://buildkite.com/oauth/device) to enter the user_code. Alternatively, send them directly to verification_uri_complete, which pre-fills the code.

The user:

  1. Enters the code (when using verification_uri).
  2. Reviews the application name and requested scopes.
  3. Selects a Buildkite organization to authorize.
  4. Approves or denies the request.

Token request

While the user completes authorization, poll the token endpoint using the device_code:

Endpoint: POST https://buildkite.com/oauth/token

POST /oauth/token HTTP/1.1
Host: buildkite.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:device_code
&client_id=your-client-id
&device_code=GmRhmhcxfnZfIQFAcZx7VFMylYeXPOO5

Request parameters

Parameter Required Description
grant_type Yes Must be urn:ietf:params:oauth:grant-type:device_code
client_id Yes The client ID of your OAuth application
device_code Yes The device_code from the device authorization response

Successful response

Once the user approves the request, the token endpoint returns:

{
  "access_token": "bkua_...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "...",
  "scope": "read_user read_organizations"
}

Use the access_token value in the Authorization header for API requests:

Authorization: Bearer bkua_...

Polling errors

While waiting for user approval, the token endpoint returns error responses until the user acts. Poll at the interval specified in the device authorization response and handle these errors:

error Description Action
authorization_pending The user has not yet approved or denied the request Continue polling at the current interval
slow_down Polling too frequently Increase the polling interval by 5 seconds and continue polling
access_denied The user denied the authorization request Stop polling and inform the user
expired_token The device code has expired Stop polling. Request a new device code to restart the flow
invalid_grant The device code is invalid or has already been used Stop polling

The initial polling interval is 5 seconds. Each slow_down response increases the required interval by 5 seconds. Respect the interval to avoid repeated slow_down responses.

Available scopes

Device authorization tokens support the same scopes as Buildkite API access tokens.

Troubleshooting

The device authorization and token endpoints return error responses with an error code and optional error_description:

{
  "error": "invalid_client"
}

Common errors

error Fix
invalid_client Check that the client_id is correct and the application is enabled. Confidential clients must also provide a valid client_secret
invalid_scope Ensure all requested scopes are valid. An empty or missing scope parameter is rejected
server_error The device authorization service is temporarily unavailable. Retry later