Implementation:OpenHands OpenHands DeviceCode
| Knowledge Sources | |
|---|---|
| Domains | Authentication, OAuth, RFC_8628 |
| Last Updated | 2026-02-11 21:00 GMT |
Overview
Concrete ORM model and status enum implementing the RFC 8628 Device Authorization Grant flow, provided by the OpenHands enterprise storage layer.
Description
The device_code module provides two classes for implementing the device authorization flow defined in RFC 8628, which enables authentication on input-constrained devices (such as CLI tools) by having the user authorize on a separate device with a browser.
The DeviceCodeStatus enum defines the possible states of a device code during its lifecycle:
- PENDING -- The device code has been created and is awaiting user authorization.
- AUTHORIZED -- The user has approved the device code on the authorization page.
- DENIED -- The user has explicitly denied the authorization request.
- EXPIRED -- The device code has exceeded its validity period without being authorized.
The DeviceCode class is a SQLAlchemy ORM model representing a device code record in the database. It contains fields for the user code (short alphanumeric string displayed to the user), the device code (longer opaque string used by the client for polling), status, expiration time, and polling metadata.
The is_expired method checks whether the device code has exceeded its configured lifetime based on the expires_at timestamp.
The is_pending method returns whether the device code is still in the PENDING state and has not yet been authorized, denied, or expired.
The authorize method transitions the device code to the AUTHORIZED state and associates it with the authorizing user's identity.
The deny method transitions the device code to the DENIED state.
The check_rate_limit method verifies that the polling client is not exceeding the allowed polling interval, returning True if the client should be told to slow down.
The update_poll_time method records the current timestamp as the last poll time, used by check_rate_limit to enforce the polling interval.
Usage
Use DeviceCode as the ORM model for persisting device authorization state. It is managed by DeviceCodeStore which provides the higher-level lifecycle operations. The DeviceCodeStatus enum is used throughout the device authorization endpoints to communicate state to clients via the standard RFC 8628 response codes.
Code Reference
Source Location
- Repository: OpenHands
- File: enterprise/storage/device_code.py
- Lines: 1-109
Signature
class DeviceCodeStatus(enum.Enum):
PENDING = "pending"
AUTHORIZED = "authorized"
DENIED = "denied"
EXPIRED = "expired"
class DeviceCode(Base):
# ORM columns
id: Mapped[int]
device_code: Mapped[str]
user_code: Mapped[str]
status: Mapped[DeviceCodeStatus]
expires_at: Mapped[datetime]
last_polled_at: Mapped[datetime | None]
user_id: Mapped[str | None]
def is_expired(self) -> bool:
...
def is_pending(self) -> bool:
...
def authorize(self, user_id: str) -> None:
...
def deny(self) -> None:
...
def check_rate_limit(self, interval_seconds: int = 5) -> bool:
...
def update_poll_time(self) -> None:
...
Import
from enterprise.storage.device_code import DeviceCode, DeviceCodeStatus
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| user_id | str |
Yes | The ID of the user authorizing the device code (for authorize) |
| interval_seconds | int |
No | Minimum polling interval in seconds; defaults to 5 (for check_rate_limit)
|
Outputs
| Name | Type | Description |
|---|---|---|
| is_expired | bool |
True if the device code has passed its expiration time (from is_expired)
|
| is_pending | bool |
True if the device code is still awaiting user action (from is_pending)
|
| slow_down | bool |
True if the client is polling too frequently and should increase its interval (from check_rate_limit)
|
State Machine
The device code follows this state machine:
[PENDING] --authorize()--> [AUTHORIZED] [PENDING] --deny()-------> [DENIED] [PENDING] --timeout------> [EXPIRED]
Once a device code leaves the PENDING state, it cannot transition to any other state.
Usage Examples
from enterprise.storage.device_code import DeviceCode, DeviceCodeStatus
# Check the state of a device code during polling
device_code = session.query(DeviceCode).filter_by(device_code="dc-abc123").first()
if device_code.is_expired():
return {"error": "expired_token"}
if device_code.check_rate_limit(interval_seconds=5):
return {"error": "slow_down"}
device_code.update_poll_time()
if device_code.is_pending():
return {"error": "authorization_pending"}
if device_code.status == DeviceCodeStatus.AUTHORIZED:
# Issue access token for the authorized user
return {"access_token": issue_token(device_code.user_id)}
if device_code.status == DeviceCodeStatus.DENIED:
return {"error": "access_denied"}