Authentication

Device auth flow, tokens, and API keys

Device Auth Flow

The OpenSkill CLI uses the device authorization flow for authentication. This is the same pattern used by GitHub CLI and other developer tools — it opens a browser to complete authentication without requiring you to paste tokens into the terminal.

Request Device Code

Start the auth flow by requesting a device code:

POST /api/auth/device/code
{
  "client_info": {
    "name": "osk-cli",
    "version": "1.0.0"
  }
}
ParameterTypeDescription
client_infoobjectOptional client information (name, version)

Response:

200 OK
{
  "device_code": "ABCD-1234",
  "user_code": "ABCD-1234",
  "verification_uri": "https://openskill.dev/auth/device",
  "expires_in": 900,
  "interval": 5
}
ParameterTypeDescription
device_code*stringCode used to poll for the token
user_code*stringCode displayed to the user
verification_uri*stringURL to open in the browser
expires_in*numberSeconds until code expires
interval*numberPolling interval in seconds

Poll for Token

After the user completes browser authentication, poll for the access token:

POST /api/auth/device/token
{
  "device_code": "ABCD-1234"
}

Response (success):

200 OK
{
  "access_token": "osk_...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "osk_rt_..."
}
If the user hasn't completed authentication yet, the endpoint returns aauthorization_pending error. Continue polling at the specified interval.

Using Tokens

Include the access token in the Authorization header:

Authorization: Bearer osk_...

Token Refresh

When the access token expires, use the refresh token to get a new one:

POST /api/auth/refresh
{
  "refresh_token": "osk_rt_..."
}

Response:

200 OK
{
  "access_token": "osk_...",
  "token_type": "Bearer",
  "expires_in": 86400,
  "refresh_token": "osk_rt_..."
}

Token Revocation

Revoke a refresh token to log out. This invalidates both the refresh token and any associated access tokens.

POST /api/auth/revoke
{
  "refresh_token": "osk_rt_..."
}

Response:

200 OK
{
  "success": true
}