January 21, 2025

QR Code Login in Keycloak: A Complete Guide

In today’s world of seamless and secure authentication methods, QR code login offers a user-friendly alternative to traditional login processes. In this guide, we'll walk you through how to integrate QR code-based login into Keycloak using WebSocket for real-time QR code generation and validation.

We’ll cover everything from the Keycloak configuration to WebSocket setup for real-time QR code generation and validation. This blog is meant to be easy to follow, fun to read, and future-proof for your authentication needs.


Why Use QR Code Authentication in Keycloak?

QR code authentication offers several advantages:

  • Easy and Fast: Users can scan a QR code instead of typing credentials.
  • Secure: Reduces the risk of keylogging and phishing attacks.
  • Future-Proof: A flexible approach for multi-factor authentication (MFA).

In this guide, we'll show how to implement a solution that generates QR codes on the frontend and validates them on the Keycloak backend, creating a seamless user experience.


Roadmap: What We Need to Do

  1. Keycloak Setup:
    • Create a custom authentication flow.
    • Implement a custom QR code authenticator.
  2. Backend Setup:
    • Set up a WebSocket server to generate QR codes in real-time.
    • Send QR code data to the frontend.
  3. Frontend Setup:
    • Use WebSocket to receive the QR code data and display it to the user.
  4. Keycloak Validation:
    • Validate the QR code on the backend and create a session for the user.
  5. Testing and Debugging:
    • Test the QR code login flow end-to-end.

Step-by-Step Implementation

1. Setting Up Keycloak for QR Code Login

Creating a Custom Authentication Flow in Keycloak

In Keycloak, you can create custom authentication flows to handle different login methods, including QR code-based authentication.

  • Go to the Keycloak Admin Console > Authentication > Flows.
  • Create a new flow (e.g., QR Code Authentication).

Create a Custom Authenticator

A custom authenticator handles the QR code validation. Here’s how to create it:


public class QRCodeAuthenticator implements Authenticator { @Override public void authenticate(AuthenticationFlowContext context) { String qrCodeData = context.getHttpRequest().getFormParameters().getFirst("qr_code_data"); if (isValidQRCode(qrCodeData)) { context.success(); // Authentication successful } else { context.challenge(context.form().setError("Invalid QR Code").createForm("qr-code-login.ftl")); } } private boolean isValidQRCode(String qrCodeData) { // Your logic to validate QR code data (e.g., token match) return qrCodeData != null && qrCodeData.equals("validToken"); } }

After this, don’t forget to register your custom authenticator in Keycloak.


2. Backend WebSocket Server for Real-Time QR Code Generation

We’ll use Spring Boot and WebSocket to generate QR codes dynamically.

Step 1: Add Dependencies

Add the following dependencies in your pom.xml file:


<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.4.0</version> </dependency>

Step 2: WebSocket Configuration


@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(qrCodeHandler(), "/qr-code").setAllowedOrigins("*"); } @Bean public WebSocketHandler qrCodeHandler() { return new QRCodeWebSocketHandler(); } }

Step 3: WebSocket Handler to Generate QR Code


public class QRCodeWebSocketHandler extends TextWebSocketHandler { @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String authToken = UUID.randomUUID().toString(); // Generate a unique token String qrCodeData = generateQRCodeData(authToken); // Generate the QR code // Send the QR code data back to the frontend session.sendMessage(new TextMessage(qrCodeData)); } private String generateQRCodeData(String token) { try { BitMatrix matrix = new MultiFormatWriter().encode(token, BarcodeFormat.QR_CODE, 200, 200); BufferedImage image = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB); for (int i = 0; i < 200; i++) { for (int j = 0; j < 200; j++) { image.setRGB(i, j, matrix.get(i, j) ? Color.BLACK.getRGB() : Color.WHITE.getRGB()); } } ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(image, "PNG", outputStream); return "data:image/png;base64," + Base64.getEncoder().encodeToString(outputStream.toByteArray()); } catch (WriterException | IOException e) { throw new RuntimeException("Error generating QR Code", e); } } }

3. Frontend: WebSocket Client to Display QR Code

On the frontend, you need a WebSocket client to connect to the backend and display the QR code in real-time.

Step 1: HTML and JavaScript


<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>QR Code Login</title> </head> <body> <h2>Scan this QR Code to Login</h2> <div id="qr-code-container"></div> <script> // Establish WebSocket connection const socket = new WebSocket("ws://localhost:8080/qr-code"); socket.onmessage = function(event) { const qrCodeData = event.data; const qrCodeContainer = document.getElementById('qr-code-container'); qrCodeContainer.innerHTML = `<img src="${qrCodeData}" alt="QR Code" />`; }; socket.onopen = function() { console.log("WebSocket connection established."); }; socket.onerror = function(error) { console.error("WebSocket Error: ", error); }; </script> </body> </html>

This code sets up a WebSocket connection to the backend, receives the QR code, and displays it as an image.


4. Keycloak QR Code Validation and Session Creation

After the user scans the QR code, Keycloak will need to validate it and create a session for the user.

Keycloak Authenticator to Validate QR Code

java
public class QRCodeAuthenticator implements Authenticator { @Override public void authenticate(AuthenticationFlowContext context) { String qrCodeData = context.getHttpRequest().getFormParameters().getFirst("qr_code_data"); if (isValidQRCode(qrCodeData)) { context.success(); // Success: User authenticated } else { context.challenge(context.form().setError("Invalid QR Code").createForm("qr-code-login.ftl")); } } private boolean isValidQRCode(String qrCodeData) { // Your validation logic here (e.g., comparing the token) return qrCodeData != null && qrCodeData.equals("validToken"); } }

Once validated, Keycloak will automatically create a user session and authenticate the user.


5. Testing and Debugging

Before going live, ensure the following:

  • Test WebSocket Connections: Ensure the frontend can successfully connect to the WebSocket server.
  • Validate QR Code: Verify that the QR code token is correctly passed to Keycloak for validation.
  • Session Creation: Confirm that Keycloak creates a session and the user is authenticated.

Conclusion

By integrating QR code authentication into Keycloak with real-time WebSocket communication, you provide a secure, efficient, and user-friendly login solution. This implementation is easy to maintain, future-proof, and ensures that your authentication process stays modern and robust.

Feel free to follow the code snippets provided above to set up the system in your own environment. With this solution, you can offer your users a seamless authentication experience while keeping security at the forefront.

Happy coding