Skip to main content

Keycloak 26.4.0: Passkeys Are Finally Production-Ready - Complete Terraform Setup Guide

·7 mins
Nicholas Meyers
Author
Nicholas Meyers
A driven Java developer with a strong focus on application security. I get a lot of energy from continuously learning and diving deep into the latest methods for making software safer. In my free time, I love working on hobby projects and exploring new technologies. Always on the lookout for the next challenge and an opportunity to keep learning and experimenting.

Passwords are the weakest link in authentication. Passkeys are offering a passwordless future that’s more secure and more user-friendly.

Introduction
#

The wait is over! Keycloak 26.4.0 has officially moved passkeys from preview to production-ready status. This milestone marks a significant step forward in authentication security, making it easier than ever to implement true passwordless authentication in your applications.

In this comprehensive guide, I’ll show you how to configure Keycloak 26.4.0 for passwordless authentication using passkeys with Infrastructure as Code via Terraform.

What Are Passkeys?
#

Passkeys are a modern, passwordless authentication method built on the FIDO2 and WebAuthn standards. Think of them as a replacement for passwords that’s both more secure and more convenient.

How Passkeys Work
#

When you create a passkey:

  1. Key Pair Generation: Your device generates a unique cryptographic key pair (public and private keys)
  2. Private Key Storage: The private key never leaves your device - it’s stored securely in your device’s hardware (TPM, Secure Enclave, etc.)
  3. Public Key Registration: Only the public key is sent to the server (in this case, Keycloak)
  4. Biometric Protection: Access to the private key is protected by biometrics (Face ID, Touch ID, Windows Hello) or a device PIN

The FIDO2 Advantage
#

FIDO2 (Fast Identity Online) is an open authentication standard developed by the FIDO Alliance and the W3C. It consists of two key components:

  • WebAuthn: The browser API that enables passwordless authentication
  • CTAP2 (Client to Authenticator Protocol): Enables communication with external authenticators like security keys

This means passkeys work with:

  • Built-in authenticators: Fingerprint sensors, facial recognition, Windows Hello
  • External authenticators: YubiKeys, Titan Security Keys, and other FIDO2-compatible devices
  • Platform authenticators: iCloud Keychain, Google Password Manager, Windows Hello

Why Passkeys Matter
#

Security Benefits
#

  1. Phishing-Resistant: Passkeys are cryptographically bound to the specific domain, making phishing attacks virtually impossible
  2. No Shared Secrets: Unlike passwords, the server never knows your private key - nothing to steal in a breach
  3. No Password Reuse: Each passkey is unique to the service, eliminating credential stuffing attacks
  4. Brute-Force Proof: There’s nothing to guess or crack - no weak passwords, no dictionary attacks

User Experience Benefits
#

  1. Faster Login: Authentication takes seconds - just a fingerprint or face scan
  2. Nothing to Remember: No more “forgot password” flows or password managers
  3. Cross-Platform: Passkeys sync across your devices through platform providers (Apple, Google, Microsoft)
  4. Consistent Experience: Same authentication method across all your devices

Statistics Worth Noting
#

According to recent industry reports:

  • 60% of data breaches involve stolen or weak passwords
  • Passkeys are 40% faster than traditional password authentication
  • Google reported 4x fewer account takeovers after implementing passkeys
  • Major adoption: Apple, Google, Microsoft, and Amazon all support passkeys

Starting Keycloak Local
#

I just wrote this small docker-compose file to start Keycloak locally. Do not use this in production - it’s configured for development only.

services:
  postgres:
    image: postgres:15
    container_name: keycloak-db
    restart: always
    environment:
      POSTGRES_DB: keycloak
      POSTGRES_USER: keycloak
      POSTGRES_PASSWORD: keycloak
    volumes:
      - keycloak_data:/var/lib/postgresql/data

  keycloak:
    image: quay.io/keycloak/keycloak:26.4
    container_name: keycloak
    command:
      - start-dev
    environment:
      KC_DB: postgres
      KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: keycloak

      KC_BOOTSTRAP_ADMIN_USERNAME: admin
      KC_BOOTSTRAP_ADMIN_PASSWORD: admin

      KC_HOSTNAME: localhost
    ports:
      - "8080:8080"
    depends_on:
      - postgres

volumes:
  keycloak_data:

Start Keycloak with docker-compose up -d

Access the admin console at http://localhost:8080/admin (admin/admin).

Configure Terraform Provider
#

Create a file provider.tf:

terraform {
  required_providers {
    keycloak = {
      source = "keycloak/keycloak"
      version = "5.5.0"
    }
  }
}

provider "keycloak" {
  client_id     = "admin-cli"
  username      = "admin"
  password      = "admin"
  url           = "http://localhost:8080"
}

Configure the Realm
#

Create your Keycloak realm:

resource "keycloak_realm" "realm" {
  realm                          = "demo"
  registration_allowed           = true
  login_with_email_allowed       = true
  duplicate_emails_allowed       = false
  registration_email_as_username = true
}

Configure the Client
#

Set up your OpenID Connect client application:

resource "keycloak_openid_client" "openid_client" {
  realm_id  = keycloak_realm.realm.id
  client_id = "fc15df10-a503-4a4a-98ea-9dc06fc2d87a"

  name    = "demo-client"
  enabled = true

  access_type         = "PUBLIC"
  valid_redirect_uris = ["https://www.keycloak.org/app/?url=http://localhost:8080&realm=demo&client=fc15df10-a503-4a4a-98ea-9dc06fc2d87a"]
  web_origins         = ["https://www.keycloak.org/app/?url=http://localhost:8080&realm=demo&client=fc15df10-a503-4a4a-98ea-9dc06fc2d87a"]

  login_theme           = "keycloak"
  standard_flow_enabled = true
}

Configure the Registration Flow
#

This is where the magic happens. We configure a custom registration flow that requires users to set up a passkey during registration:

resource "keycloak_authentication_flow" "registration_passwordless" {
  realm_id    = keycloak_realm.realm.id
  alias       = "passwordless registration"
  description = "Passwordless registration flow"
  provider_id = "basic-flow"
}


resource "keycloak_authentication_subflow" "passwordless_step" {
  realm_id          = keycloak_realm.realm.id
  parent_flow_alias = keycloak_authentication_flow.registration_passwordless.alias

  alias         = "passwordless registration form"
  provider_id   = "form-flow"
  authenticator = "registration-page-form"
  requirement   = "REQUIRED"
}


resource "keycloak_authentication_execution" "user_creation" {
  realm_id          = keycloak_realm.realm.id
  parent_flow_alias = keycloak_authentication_subflow.passwordless_step.alias

  authenticator = "registration-user-creation"
  requirement   = "REQUIRED"
  priority      = 0
}

resource "keycloak_authentication_bindings" "registration_flow_realm_bindings" {
  realm_id          = keycloak_realm.realm.id
  registration_flow = keycloak_authentication_flow.registration_passwordless.alias
}

resource "keycloak_required_action" "webauthn_register_passwordless" {
  realm_id       = keycloak_realm.realm.id
  alias          = "webauthn-register-passwordless"
  name           = "Webauthn Register Passwordless"
  enabled        = true
  default_action = true
}

Configure the Sign In Flow
#

Now configure the sign in flow:

resource "keycloak_authentication_flow" "sign_in_passwordless" {
  realm_id    = keycloak_realm.realm.id
  alias       = "passwordless sign in"
  description = "Passwordless sign in flow"
  provider_id = "basic-flow"
}


resource "keycloak_authentication_subflow" "sign_in_step" {
  realm_id          = keycloak_realm.realm.id
  parent_flow_alias = keycloak_authentication_flow.sign_in_passwordless.alias

  alias       = "passwordless sign in subflow"
  provider_id = "basic-flow"
  requirement = "REQUIRED"
}


resource "keycloak_authentication_execution" "passwordless_webauthn_execution" {
  realm_id          = keycloak_realm.realm.id
  parent_flow_alias = keycloak_authentication_subflow.sign_in_step.alias

  authenticator = "webauthn-authenticator-passwordless"
  requirement   = "REQUIRED"
  priority      = 0
}


resource "keycloak_authentication_bindings" "browser_flow_realm_bindings" {
  realm_id     = keycloak_realm.realm.id
  browser_flow = keycloak_authentication_flow.sign_in_passwordless.alias
}

UI Configuration Alternative
#

If you prefer to configure Keycloak through the Admin UI instead of using Terraform, follow these detailed steps. This approach is useful for learning, testing, or when Terraform isn’t available in your workflow.

Create a new realm and client
#

  1. Create a Realm:

    • Log in to the Keycloak Admin Console at http://localhost:8080
    • Click the realm dropdown in the top-left corner (it says “Keycloak” by default)
    • Click Create Realm
    • Set the Realm name to demo (or your preferred name)
    • Enable User registration under the Login tab
    • Configure Email as username and disable Duplicate emails if desired
    • Click Create
  2. Create a Client:

    • Navigate to Clients in the left sidebar
    • Click Create client
    • Set Client type to OpenID Connect
    • Enter a Client ID (e.g., fc15df10-a503-4a4a-98ea-9dc06fc2d87a)
    • Click Next
    • Enable Standard flow (Authorization Code Flow)
    • Set Access type to Public (for frontend applications)
    • Click Save
    • In the client settings, add your application’s URL to Valid redirect URIs (e.g., https://www.keycloak.org/app/?url=http://localhost:8080&realm=demo&client=fc15df10-a503-4a4a-98ea-9dc06fc2d87a)
    • Add the same URL to Web origins for CORS support

Create new flows
#

Navigate to the realm’s Authentication tab and create a new Registration Flow and a new Sign In Flow.

Keycloak authentication overview

Create a new flow for registration
#

  1. Click the Flows tab in the Authentication section
  2. Click Create flow
  3. Configure the registration flow:
    • Name: passwordless registration
    • Description: Passwordless registration flow
    • Top level flow type: Basic flow
  4. Click Create
  5. In your new flow, click Add a sub-flow
  6. Fill in a name and select Form as the flow type
  7. Mark this flow as Required
  8. In the form subflow, click +-Sign and select Add execution
  9. Select Registration User Profile Creation and click Add
  10. Mark this step as Required

Keycloak passwordless registration

Bind the registration flow:

  • Go to the Bind flow tab at the top
  • Change the dropdown to Registration flow
  • Click Save

Create a new flow for sign in
#

  1. Return to the Flows tab
  2. Click Create flow again
  3. Configure the sign in flow:
    • Name: passwordless sign in
    • Description: Passwordless sign in flow
    • Top level flow type: Basic flow
  4. Click Create
  5. In your new flow, click Add a sub-flow
  6. Fill in a name and select Generic as the flow type
  7. Mark this flow as Required
  8. In the generic subflow, click +-Sign and select Add execution
  9. Select WebAuthn Passwordless Authenticator and click Add
  10. Mark this step as Required

Keycloak passwordless sign in

Bind the sign in flow:

  • Go to the Bind flow tab at the top
  • Change the dropdown to Browser flow
  • Click Save

Enable default required actions
#

  1. Navigate to AuthenticationRequired actions tab
  2. Find Webauthn Register Passwordless in the list
  3. Enable the following checkboxes:
    • Enabled: Makes the action available
    • Default action: Automatically prompts new users to register a passkey during their first login

Keycloak default required actions

Test it Out
#

Open your browser and navigate to https://www.keycloak.org/app/. You should see a page where you can configure your test setup. Fill in the realm and client details, and click Save.

Conclusion
#

Keycloak 26.4.0’s production-ready passkey support marks a turning point in authentication security. With passkeys now out of preview, there’s no reason to delay implementing passwordless authentication in your applications.

The combination of Keycloak’s robust authentication platform and Terraform’s Infrastructure as Code approach gives you:

Reproducible deployments across environments
Version-controlled configuration for audit and rollback
Enterprise-grade security with industry standards
Excellent user experience with fast, modern authentication
Future-proof architecture aligned with industry direction