
RFC 8628: The Device Authorization Grant
An examination of RFC 8628, the Device Authorization Grant, which enables users on devices without browsers to authenticate with OAuth 2.0 providers.
OAuth 2.0 was originally designed around the assumption that the client application could open a system browser, redirect the user to an identity provider, and capture the authorization code via a local redirect URI. This model works perfectly for web applications and standard mobile apps. However, it fails completely for "headless" devices: command-line interfaces (CLIs), smart TVs, gaming consoles, and Internet of Things (IoT) sensors. These devices often lack a browser, a keyboard, or a reliable method to capture HTTP redirects.
Part 3 of the OAuth 2.0 RFCs Every Engineer Should Read series, RFC 8628, titled "OAuth 2.0 Device Authorization Grant," provides the mechanism to solve this input problem. It introduces a new grant type that allows a device with limited input capabilities to initiate an authentication flow by prompting the user to complete the action on a separate, more capable device.
The Core Problem: The Input Channel Mismatch
To understand why RFC 8628 is necessary, we must look at the data flow of a standard OAuth 2.0 Authorization Code Grant. In the standard flow, the client app opens a browser window. The user logs in and consents. The browser redirects back to the client app with an authorization code. The client app then exchanges this code for tokens.
In this model, the authentication channel (where the user enters credentials) and the execution channel (where the app runs) are the same device. For a CLI tool running in a terminal, or a smart thermostat, the execution channel exists, but the authentication channel does not. You cannot easily display a QR code or a login page on a terminal without external dependencies, and you cannot reliably capture a redirect URI in a background IoT service.
RFC 8628 decouples these channels. It forces the authentication to happen on a "second screen" (a phone or desktop browser), while the device itself acts only as a listener waiting for the result. This separation is the defining characteristic of the oauth 2.0 device flow, distinguishing it from direct client-server interactions where the client handles the user interface directly.
The Mechanism: A Two-Step Handshake
The Device Authorization Grant operates through a specific sequence of API calls between the Device Client, the Authorization Server (AS), and the User’s Browser. Let’s trace this with named actors: Terminal (the device), AuthServer (the OAuth provider), and User (the human).
Step 1: Requesting the Device Code
The Terminal initiates the flow by sending a POST request to the Authorization Server’s /device_authorization endpoint. This request includes the client identifier (client_id) and optionally the requested scopes.
POST /device_authorization HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
client_id=s6BhdRkqt3&scope=openid+profileThe AuthServer responds with a JSON object containing several critical pieces of information:
device_code: A unique, secret string generated by the server. This code links the pending authentication session to the specific polling device.user_code: A short, human-readable string (e.g.,XJX-CZPL). This is what the user types into their browser.verification_uri: The URL the user must visit.expires_in: The lifetime of thedevice_codein seconds.interval: The minimum number of seconds theTerminalshould wait between polling requests.
{
"device_code": "GmRhmhcxvTcEwKiDkqVsWg",
"user_code": "XJX-CZPL",
"verification_uri": "https://auth.example.com/device",
"expires_in": 1800,
"interval": 5
}The AuthServer stores the device_code in a stateful store, marking the session as authorization_pending. At this point, the Terminal has no token. It has only a ticket.
Step 2: The Polling Loop
The Terminal now enters a polling loop. It repeatedly sends a POST request to the /token endpoint using the grant_type=urn:ietf:params:oauth:grant-type:device_code.
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=GmRhmhcxvTcEwKiDkqVsWgThe AuthServer checks the status of the device_code. If the user has not yet visited the verification URI, the server responds with:
{
"error": "authorization_pending",
"error_description": "The device is waiting for user authorization."
}The Terminal waits for the interval value provided in the initial device authorization response before polling again. This prevents hammering the server. The Terminal sleeps for interval seconds and retries.
Step 3: User Consent
While the Terminal polls, the User opens their browser and navigates to https://auth.example.com/device. They enter the user_code (XJX-CZPL).
The AuthServer maps the user_code to the active device_code session. It then prompts the user to log in (if not already authenticated) and consent to the requested scopes. Upon consent, the AuthServer updates the session state from authorization_pending to authorized.
Step 4: Token Issuance
The next time the Terminal polls, the AuthServer sees the state is authorized. It responds with the standard OAuth 2.0 token response:
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"scope": "openid profile"
}The Terminal now has the credentials it needs to act on behalf of the user. The loop terminates.
Security Considerations
The Device Authorization Grant introduces specific security mechanisms to prevent abuse, primarily because the device_code is transmitted over the network and the user_code is human-entered.
Brute Force Mitigation
The user_code is typically short (e.g., 8 characters, alphanumeric). This is not cryptographically strong against brute force if an attacker can guess all possible combinations. However, RFC 8628 mitigates this through several layers:
- Rate Limiting: The
AuthServermust implement strict rate limiting on the/tokenendpoint for thedevice_codegrant type. If theTerminalpolls too frequently, the server returns aslow_downerror. This error instructs the client to increase its current polling interval by 5 seconds, enforcing a backoff strategy rather than providing a new interval value in the response body. - Short Expiration: The
device_codeanduser_codehave short lifetimes (typically 15–30 minutes). This limits the window of opportunity for an attack. - Single Use: Once a
device_codeis used to obtain a token, it is invalidated. Reusing it will result in aninvalid_granterror.
Phishing Risks
The reliance on the user to manually enter a code introduces a phishing vector. An attacker could create a fake login page that mimics the AuthServer's verification URI. If the user enters their credentials on the fake site, the attacker gains access.
However, this risk is inherent to any password-based authentication. RFC 8628 does not eliminate phishing; it merely shifts the interface. To mitigate this, users should be trained to verify the domain in the browser's address bar. Additionally, modern implementations often use QR codes as a user interface pattern to facilitate the device flow. By encoding the verification_uri and user_code into a scannable image, mobile apps can reduce manual entry errors and streamline the process, though QR codes themselves are not a protocol feature defined by the RFC.
Why Not Just Use PKCE?
You might ask: "Why not use the Authorization Code Grant with PKCE (Proof Key for Code Exchange) on the device?"
PKCE is indeed the recommended pattern for public clients in OAuth 2.1. However, PKCE still relies on the client being able to receive an HTTP redirect. On a headless IoT device or a CLI, capturing that redirect is technically challenging or impossible without a local proxy or browser.
RFC 8628 is specifically designed for scenarios where no browser interaction is possible on the device itself. It is the preferred choice for iot authentication scenarios where the device lacks the network stack sophistication or UI capabilities to handle standard redirects. It is the only standardized way to handle authentication on truly headless devices today.
The Future: Deprecation and Alternatives
It is important to note that the OAuth 2.1 working group draft has discussed the potential deprecation of the Device Authorization Grant in favor of more modern patterns. This remains a discussion point within the working group and is not a finalized deprecation. The primary criticism is that the polling loop is inefficient and prone to race conditions.
Alternative approaches include:
- QR Code Scanning with Mobile Apps: Many modern CLIs use a QR code that encodes a short-lived URL. The mobile app scans the QR code, opens a browser, and completes the OAuth flow. This is essentially a user-friendly wrapper around the device flow, but it shifts the polling logic to the mobile app rather than the server.
- Localhost Redirect with Port Forwarding: Some CLI tools start a local HTTP server on a high port. They open the browser to that local address. While this looks like the standard flow, it requires the device to have a network interface and a way to bind to a port, which some restricted IoT environments do not allow.
Despite these alternatives, RFC 8628 remains the foundational specification for device authentication. It provides a clear, standardized API for interoperability between different OAuth providers and device clients.
Conclusion
RFC 8628 solves a fundamental mismatch in OAuth 2.0: the assumption that the client can display a browser. By introducing a two-channel architecture—where the device polls and the user consents on a second screen—it enables secure authentication for headless devices.
For backend and IoT developers, understanding this flow is critical. It allows you to build CLIs, smart devices, and embedded systems that can securely interact with OAuth 2.0 providers without needing to embed a full browser stack. The trade-off is increased complexity in the polling loop and a slightly more cumbersome user experience, but the security benefits and technical feasibility make it an indispensable tool in the OAuth toolkit.
Related posts
RFC 6749 Revisited: What Still Applies in 2026
An examination of RFC 6749 (OAuth 2.0) in 2026, analyzing which grant types remain relevant and why the implicit flow is deprecated.
GraphQL Security: Authentication and Authorization Patterns
An examination of GraphQL security patterns for authentication and authorization, including query complexity limits and API security best practices.
RFC 7636: PKCE and the Authorization Code Interception Attack
RFC 7636 defines PKCE to prevent authorization code interception attacks in OAuth 2.0 flows for public clients.