Keycloak for Smart Devices: A Practical Guide to Device Code Flow 1/2

In this post, we’ll implement Device Code Flow with Keycloak using Spring Boot and Flutter.

· Prerequisites
· Overview
∘ What is device code flow?
∘ Device Authorization Flow
∘ Why Use Keycloak for Device Code Flow?
· Architecture Overview
∘ The Components
∘ The Data Flow Explained
· Implementing Device Code Flow: Step-by-Step
∘ Step 1: Setting Up Keycloak
· Conclusion
· References


Prerequisites

This is the list of all the prerequisites:

  • Spring Boot 3 (for Gateway).
  • Java 21+ and Maven 3.6.3 or later
  • Flutter 3+ installed (for the Android app)
  • An installed Keycloak (v26+) server
  • Android Studio, Visual Studio Code, or another IDE

⚠️ Note: This post is based on Quarkus-based Keycloak distribution (Keycloak 26+).

Overview

Have you ever tried to sign into an app on your Smart TV, game console, or a command-line tool (CLI)? Typing a long password with a remote control or on a device with no keyboard is a nightmare. This is a common challenge in the world of Internet of Things (IoT) and embedded devices.

So, how do we solve it? The answer is the OAuth 2.0 Device Authorization Grant, more commonly known as the Device Code Flow.

In this post, we’ll break down what Device Code Flow is and show you how to implement it using Keycloak, Spring Boot, and Flutter.

What is device code flow?

The OAuth 2.0 device authorization grant is part of OAuth 2.0 (RFC 8628) for devices with limited input capabilities. It is designed for Internet-connected devices that either lack a browser to perform a user-agent- based authorization or are input constrained to the extent that requiring the user to input text in order to authenticate during the authorization flow is impractical. It enables OAuth clients on such devices (like smart TVs, media consoles, digital picture frames, and printers) to obtain user authorization to access protected resources by using a user agent on a separate device.

https://datatracker.ietf.org/doc/html/rfc8628

Device Authorization Flow

The device authorization flow illustrated in Figure 1 includes the following steps:

(A) The client requests access from the authorization server and includes its client identifier in the request.

(B) The authorization server issues a device code and an end-user code and provides the end-user verification URI.

(C) The client instructs the end user to use a user agent on another device and visit the provided end-user verification URI. The client provides the user with the end-user code to enter in order to review the authorization request.

(D) The authorization server authenticates the end user (via the user agent) and prompts the user to input the user code provided by the device client. The authorization server validates the user code provided by the user, and prompts the user to accept or decline the request.

(E) While the end user reviews the client’s request (step D), the client repeatedly polls the authorization server to find out if the user completed the user authorization step. The client includes the device code and its client identifier.

(F) The authorization server validates the device code provided by the client and responds with the access token if the client is granted access, an error if they are denied access, or an indication that the client should continue to poll.

Why Use Keycloak for Device Code Flow?

Keycloak offers several advantages for implementing Device Code Flow:

  • Built-in Support: Native implementation of OAuth 2.0 Device Authorization Grant
  • Customizable UI: Flexible theming for the verification page
  • Enterprise Features: Client policies, user federation, multi-factor authentication
  • Protocol Flexibility: Support for OpenID Connect, SAML, and other protocols
  • Open Source: No licensing costs with full feature access

Architecture Overview

Here’s how our system will work:

+------------+          +-------------+          +------------+          +----------------------+
| Flutter TV | -------> | Spring Cloud| -------> | Keycloak | -------> | User's |
| App | <------- | Gateway | <------- | Server | <------- | Phone or Web browser |
+------------+ +-------------+ +------------+ +----------------------+
| | |
| (Polls for token) | (Validates token) | (Displays code)
| | |
v v v
+------------+ +-------------+ +------------+
| Gets Token | | Accesses | | User logs |
| & Accesses | | Backend | | in & grants|
| APIs | | Services | | consent |
+------------+ +-------------+ +------------+

The Components

  1. Flutter TV App: The client application running on the smart device (Android TV). It has no keyboard and relies on the Device Flow.
  2. Spring Cloud Gateway: The single entry point for all API traffic. It acts as a security guard, validating every request.
  3. Keycloak Server: The central authentication server. It issues tokens, manages users, and handles the device flow logic.
  4. User’s Phone/Laptop (Web Browser): The user’s secondary device with a proper browser, used to complete the login approval.

The Data Flow Explained

Step 1: App Requests a Device Code

  • PathFlutter TV App -> Spring Cloud Gateway -> Keycloak Server
  • Action: The TV app initiates the login process. It asks Keycloak, “I’m a device, please give me a code for a user to approve.
  • Technical Detail: The app makes a POST request to Keycloak’s device authorization endpoint (/auth/device). It identifies itself with its client_id and client_secret.

Step 2: Keycloak Provides Codes

  • PathKeycloak Server -> Spring Cloud Gateway -> Flutter TV App
  • Action: Keycloak responds with a device_code (for the app) and a user_code (for the user). It also provides the verification_uri.
  • Technical Detail: The app receives this JSON payload and now displays the user_code and verification_uri (or a QR code for the verification_uri_complete) on the TV screen.

The response will be a JSON object containing:

{
"device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS",
"user_code": "WDJB-MJHT",
"verification_uri": "https://keycloak.example.com/realms/myrealm/device",
"verification_uri_complete": "https://keycloak.example.com/realms/myrealm/device?user_code=WDJB-MJHT",
"expires_in": 600,
"interval": 5
}
  • device_code: The code the app uses to poll for the token.
  • user_code: The code the user enters on the verification page.
  • verification_uri: The URL the user needs to visit.
  • verification_uri_complete: A full URL that includes the user code (great for QR codes!).
  • expires_in: How long are the codes valid?
  • interval: The recommended polling interval (in seconds).

Your Flutter UI should display the user_code and verification_uri_complete (perhaps as a QR code) to the user.

Step 3: User Grants Approval

  • PathUser's Phone or Web Browser -> Keycloak Server
  • Action: The user sees the code on their TV, goes to the verification URL on their phone or web browser, and enters the code. They then log in to Keycloak (on their phone) aUser’snd grant permission to the TV app.
  • Technical Detail: This step happens entirely between the user’s browser and Keycloak. The TV app is not involved. Keycloak now knows that the user_code has been authorized.

Step 4: App Polls for Token

  • PathFlutter TV App -> Spring Cloud Gateway -> Keycloak Server
  • Action: While the user is logging in on their phone or web browser, the TV app is repeatedly asking Keycloak: “Has the user approved my device_code yet?
  • Technical Detail: The app polls Keycloak’s token endpoint (/token) using the device_code and the specific grant type urn:ietf:params:oauth:grant-type:device_code. Initially, Keycloak responds with “authorization_pending.” Once the user completes Step 3, Keycloak responds with the coveted Access Token and Refresh Token.
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI...",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_expires_in": 1800,
"scope": "openid profile email"
}

At this point, the Flutter TV App is successfully authenticated and holds a valid JWT (JSON Web Token) from Keycloak.

Implementing Device Code Flow: Step-by-Step

Step 1: Setting Up Keycloak

First, let’s set up our Keycloak client for the device flow.

Create a Realm

This post will use the same realm as used in our previous post:

Configure a Client

  1. Go to Clients in the left sidebar
  2. Click Create Client
  3. Configure the following settings:
  • Client Type: OpenID Connect
  • Client IDsmart-device-client (or your preferred identifier)

4. In the Capability config section, enable:

  • Client Authentication: ON
  • Standard Flow: OFF
  • Direct Access Grants: OFF
  • OAuth 2.0 Device Authorization Grant: ON
  • Set the Valid Redirect URIs if your device needs callbacks (optional for pure Device Flow)

5. Add Users: Create test users in the realm for authentication.

The Device Flow has a special OAuth scope named urn:resourcserver:api:scopes:read. You can create custom scopes to define the permissions your TV app needs.

Your Keycloak instance is now ready for Device Code Flow.

Conclusion

The second part of this post, which explains how to implement secure token validation in Spring Cloud Gateway and how to integrate Device Code Flow in Flutter for Android TV, is available here.

Support me through GitHub Sponsors.

Thank you for reading!! See you in the next post.

References

👉Link to Medium blog

Related Posts