OAuth Device Flow: Auth for Smart TV ChatGPT Apps 2026

Building a ChatGPT app for Smart TVs, game consoles, or IoT devices presents a unique authentication challenge: how do users log in when typing is painful or impossible? Traditional OAuth flows require complex form inputs on devices with limited keyboards—a frustrating user experience that leads to abandonment.

OAuth Device Authorization Flow (RFC 8628) solves this problem elegantly. Instead of forcing users to type emails and passwords with a TV remote, the device displays a short user code or QR code. Users then visit a simple URL on their smartphone or computer, enter the code, and authorize the device. The ChatGPT app on the TV polls your authorization server and receives access tokens once approved—no painful typing required.

This authentication pattern is critical for ChatGPT apps targeting Smart TVs (Roku, Apple TV, Samsung, LG), game consoles (PlayStation, Xbox), streaming devices (Chromecast, Fire TV), and IoT devices (smart displays, wearables). In this comprehensive guide, you'll learn how to implement device flow authentication with best practices for user experience, security, and OpenAI Apps SDK compliance.

For a complete overview of OAuth 2.1 for ChatGPT apps, see our OAuth 2.1 for ChatGPT Apps: Complete 2026 Implementation Guide.


Understanding Device Authorization Flow

What is Device Authorization Flow?

Device Authorization Flow is an OAuth 2.0 extension specifically designed for input-constrained devices. Unlike traditional authorization code flow (which requires redirects and form inputs), device flow separates the authorization step from the device itself.

The flow works in four steps:

  1. Device requests a device code from your authorization server
  2. Device displays a user code and verification URL (as text or QR code)
  3. User visits the URL on a separate device (phone/computer) and enters the code
  4. Device polls the authorization server until the user completes authorization, then receives access and refresh tokens

This pattern provides a seamless cross-device authentication experience without requiring users to type complex credentials on a TV remote or game controller.

Real-World Use Cases

Device flow is essential for ChatGPT apps running on:

  • Smart TVs: Roku, Apple TV, Samsung Tizen, LG webOS, Android TV
  • Game Consoles: PlayStation 5, Xbox Series X/S, Nintendo Switch
  • Streaming Devices: Chromecast, Fire TV Stick, Roku Stick
  • IoT Devices: Smart displays (Echo Show), fitness equipment, car infotainment systems
  • Wearables: Smartwatches with limited input capabilities

For example, a fitness studio ChatGPT app on a Smart TV might allow users to browse class schedules, book sessions, and access personalized workout plans—all authenticated via their phone in seconds.


Implementing Device Flow: Step-by-Step

Step 1: Device Code Request

When your ChatGPT app launches on a Smart TV, the first step is requesting a device code from your OAuth authorization server.

Device Code Request (POST /oauth/device/code):

// Smart TV ChatGPT app requests device code
async function requestDeviceCode() {
  const response = await fetch('https://auth.yourdomain.com/oauth/device/code', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: new URLSearchParams({
      client_id: 'your-chatgpt-app-client-id',
      scope: 'openid profile email chatgpt:read chatgpt:write',
    }),
  });

  const data = await response.json();

  return {
    device_code: data.device_code,        // Used for polling (opaque, never shown to user)
    user_code: data.user_code,            // Short code shown to user (e.g., "ABCD-1234")
    verification_uri: data.verification_uri, // URL user visits (e.g., "https://auth.yourdomain.com/device")
    verification_uri_complete: data.verification_uri_complete, // Optional: URI with code pre-filled
    expires_in: data.expires_in,          // Code expiration (typically 600 seconds / 10 minutes)
    interval: data.interval,              // Polling interval in seconds (typically 5)
  };
}

Response Example:

{
  "device_code": "9e9e8d7c-5b4a-3c2d-1e0f-9a8b7c6d5e4f",
  "user_code": "WDJB-MJHT",
  "verification_uri": "https://auth.yourdomain.com/device",
  "verification_uri_complete": "https://auth.yourdomain.com/device?user_code=WDJB-MJHT",
  "expires_in": 600,
  "interval": 5
}

Step 2: Display User Code and QR Code

Once you receive the device code response, display the user code and verification URL prominently on the Smart TV screen.

Best Practices:

  1. Generate a QR code linking to verification_uri_complete for instant mobile scanning
  2. Display the short user code (e.g., "WDJB-MJHT") in large, high-contrast text
  3. Show the verification URL (e.g., "auth.yourdomain.com/device") for manual entry
  4. Add a countdown timer showing code expiration (10 minutes)

QR Code Generation Example:

import QRCode from 'qrcode';

async function displayAuthScreen(deviceData) {
  // Generate QR code as data URL
  const qrCodeDataURL = await QRCode.toDataURL(deviceData.verification_uri_complete, {
    width: 300,
    margin: 2,
    color: {
      dark: '#000000',
      light: '#FFFFFF',
    },
  });

  // Display on Smart TV UI (pseudo-code)
  tvScreen.render(`
    <div class="auth-screen">
      <h1>Log In to ChatGPT App</h1>

      <!-- QR Code (fastest method) -->
      <div class="qr-section">
        <img src="${qrCodeDataURL}" alt="Scan to authenticate" />
        <p>Scan with your phone camera</p>
      </div>

      <!-- Manual Code Entry (fallback) -->
      <div class="code-section">
        <p>Or visit: <strong>${deviceData.verification_uri}</strong></p>
        <p class="user-code">${deviceData.user_code}</p>
        <p>Enter this code to continue</p>
      </div>

      <!-- Countdown Timer -->
      <p class="timer">Code expires in ${formatCountdown(deviceData.expires_in)}</p>
    </div>
  `);
}

function formatCountdown(seconds) {
  const mins = Math.floor(seconds / 60);
  const secs = seconds % 60;
  return `${mins}:${secs.toString().padStart(2, '0')}`;
}

For detailed QR code security considerations, see our ChatGPT App Security: Complete 2026 Implementation Guide.

Step 3: Poll for Authorization

While the user completes authorization on their phone, your Smart TV app polls the authorization server at regular intervals to check if the user has granted access.

Polling with Exponential Backoff:

async function pollForToken(deviceCode, interval, expiresIn) {
  const startTime = Date.now();
  const expirationTime = startTime + (expiresIn * 1000);
  let currentInterval = interval * 1000; // Convert to milliseconds

  while (Date.now() < expirationTime) {
    try {
      const response = await fetch('https://auth.yourdomain.com/oauth/token', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        body: new URLSearchParams({
          grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
          device_code: deviceCode,
          client_id: 'your-chatgpt-app-client-id',
        }),
      });

      const data = await response.json();

      // SUCCESS: User authorized, tokens received
      if (response.ok) {
        return {
          access_token: data.access_token,
          refresh_token: data.refresh_token,
          expires_in: data.expires_in,
          token_type: data.token_type,
        };
      }

      // Handle specific error codes
      if (data.error === 'authorization_pending') {
        // User hasn't completed authorization yet, continue polling
        await sleep(currentInterval);
        continue;
      }

      if (data.error === 'slow_down') {
        // Server requests slower polling, increase interval by 5 seconds
        currentInterval += 5000;
        await sleep(currentInterval);
        continue;
      }

      if (data.error === 'access_denied') {
        // User explicitly denied authorization
        throw new Error('User denied authorization');
      }

      if (data.error === 'expired_token') {
        // Device code expired (10 minutes elapsed)
        throw new Error('Authorization code expired. Please try again.');
      }

      // Unknown error
      throw new Error(`Authorization failed: ${data.error}`);

    } catch (error) {
      console.error('Polling error:', error);
      throw error;
    }
  }

  // Timeout reached
  throw new Error('Authorization timeout. Please try again.');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Polling Best Practices:

  • Respect the interval parameter (typically 5 seconds) from the device code response
  • Implement exponential backoff if the server returns slow_down error
  • Stop polling after expires_in (typically 600 seconds / 10 minutes)
  • Handle authorization_pending gracefully—this is the expected state while the user is authorizing
  • Never poll faster than the server's specified interval (prevents rate limiting)

Step 4: Token Exchange and Storage

Once polling succeeds, your Smart TV app receives access and refresh tokens. Store these securely using the device's secure storage APIs.

Secure Token Storage on Smart TVs:

// Token storage example (platform-specific APIs vary)
async function storeTokensSecurely(tokens) {
  // Use platform-specific secure storage
  // - Roku: roRegistrySection with encryption
  // - Apple TV: Keychain Services
  // - Android TV: EncryptedSharedPreferences
  // - Samsung Tizen: Secure Storage API

  await secureStorage.set('access_token', tokens.access_token);
  await secureStorage.set('refresh_token', tokens.refresh_token);
  await secureStorage.set('token_expiry', Date.now() + (tokens.expires_in * 1000));

  console.log('Tokens stored securely');
}

// Retrieve tokens for authenticated requests
async function getAccessToken() {
  const token = await secureStorage.get('access_token');
  const expiry = await secureStorage.get('token_expiry');

  // Check if token is expired
  if (Date.now() >= expiry) {
    // Refresh token (see OAuth 2.1 guide for refresh flow)
    return await refreshAccessToken();
  }

  return token;
}

For comprehensive token management patterns, see our OAuth 2.1 for ChatGPT Apps: Complete 2026 Implementation Guide.


UX Best Practices for Device Flow

1. QR Code Generation

QR codes provide the fastest authentication path for users with smartphones. Optimize QR code generation for maximum compatibility:

  • Use high error correction (Level H: 30% recovery) to handle screen glare and camera focus issues
  • Minimum size: 300x300 pixels for Smart TV displays (visible from couch distance)
  • Embed verification_uri_complete (URL with pre-filled user code) to eliminate manual typing
  • Test with multiple camera apps (iPhone Camera, Android Camera, QR scanner apps)

Library Recommendation:

  • Node.js/React: qrcode (lightweight, well-maintained)
  • Vanilla JS: qrcodejs (no dependencies)

2. Short User Codes

If users choose manual entry instead of QR scanning, user codes must be short and unambiguous:

  • 8 characters maximum (e.g., "WDJB-MJHT" with hyphen separator for readability)
  • Use uppercase letters and digits only (avoid lowercase to prevent I/l/1 confusion)
  • Exclude ambiguous characters: Omit 0/O, 1/I/l, 5/S, 8/B to prevent entry errors
  • Group with hyphens (4-4 format: "ABCD-1234" is easier than "ABCD1234")

Example User Code Generator:

function generateUserCode() {
  const chars = 'ACDEFGHJKLMNPQRTUVWXY2346789'; // Excludes ambiguous chars
  let code = '';

  for (let i = 0; i < 8; i++) {
    if (i === 4) code += '-'; // Add hyphen separator
    code += chars[Math.floor(Math.random() * chars.length)];
  }

  return code; // Example: "WDJB-MJHT"
}

3. Polling Interval Optimization

Balance responsiveness (fast token delivery) with server load (avoid excessive requests):

  • Default interval: 5 seconds (recommended by RFC 8628)
  • Increase interval by 5 seconds when server returns slow_down error
  • Maximum interval: 30 seconds (prevents users waiting too long)
  • Stop polling after expiration (typically 600 seconds / 10 minutes)

4. Timeout and Error Handling

Provide clear feedback when authentication fails or times out:

  • Display countdown timer showing code expiration (10 minutes)
  • Show "Waiting for authorization..." status during polling
  • Handle access_denied: "Authorization denied. Please try again or contact support."
  • Handle expired_token: "Code expired. Generating new code..." (auto-restart flow)
  • Network errors: "Connection lost. Retrying..." (automatic retry with exponential backoff)

Security Best Practices

1. Device Code Expiration

Short expiration windows (10 minutes) prevent attackers from using intercepted device codes after the legitimate session ends.

  • Default expiration: 600 seconds (10 minutes)
  • Minimum: 300 seconds (5 minutes) for high-security apps
  • Maximum: 1800 seconds (30 minutes) for complex authorization flows

2. Rate Limiting Polling Requests

Prevent brute force attacks and denial-of-service (DoS) by rate limiting polling requests:

  • Limit: 1 request per 5 seconds per device code (enforce interval parameter)
  • Block device codes after 50 failed attempts (prevents brute force)
  • Return slow_down error if polling exceeds rate limit
  • Log suspicious activity (multiple device codes from same IP)

For advanced rate limiting strategies, see our ChatGPT App Security: Complete 2026 Implementation Guide.

3. Prevent Device Code Enumeration

Device codes should be cryptographically random and infeasible to guess:

  • Minimum entropy: 128 bits (e.g., UUID v4: 9e9e8d7c-5b4a-3c2d-1e0f-9a8b7c6d5e4f)
  • Use secure random generators (e.g., crypto.randomUUID() in Node.js, SecureRandom in Java)
  • Never use sequential IDs or timestamps as device codes

4. PKCE Extension for Device Flow

While PKCE (Proof Key for Code Exchange) is standard for authorization code flow, it's not natively part of device flow. However, you can enhance security by requiring PKCE-like challenge/verifier pairs:

  1. Device generates code_verifier (random string) and code_challenge (SHA-256 hash)
  2. Device includes code_challenge in initial device code request
  3. Device includes code_verifier in token request
  4. Server validates match before issuing tokens

This prevents authorization code interception attacks even if an attacker compromises the device code.


Implementation Checklist

Device Flow Implementation (Smart TV/IoT ChatGPT Apps):

  • Device Code Request: POST /oauth/device/code with client_id and scope
  • QR Code Generation: Use qrcode library with high error correction (Level H)
  • User Code Display: 8-character format (e.g., "WDJB-MJHT"), exclude ambiguous chars
  • Polling with Exponential Backoff: Start at 5 seconds, increase on slow_down
  • Error Handling: authorization_pending, slow_down, access_denied, expired_token
  • Secure Token Storage: Platform-specific secure storage (Keychain, EncryptedSharedPreferences)
  • Token Refresh: Implement refresh token flow for long-lived sessions
  • Security: 128-bit device codes, rate limiting, 10-minute expiration
  • UX: Countdown timer, clear status messages, auto-restart on expiration
  • OpenAI Compliance: Test device flow with ChatGPT developer mode

Related Resources

Internal Links:

External Links:


Start Building Device Flow Authentication

Ready to implement seamless cross-device authentication for your Smart TV or IoT ChatGPT app? Sign up for MakeAIHQ and generate production-ready OAuth device flow code with our AI Conversational Editor.

No coding required. Just describe your authentication requirements, and our platform generates:

  • ✅ Complete device flow implementation (device code request, polling, token exchange)
  • ✅ QR code generation with optimal settings
  • ✅ Secure token storage for your target platform
  • ✅ OpenAI Apps SDK-compliant MCP server
  • ✅ One-click deployment to ChatGPT Store

Start Your Free Trial →


Published: December 25, 2026 Author: MakeAIHQ Team Category: Authentication, OAuth, Security