Skip to content

ba-itsys/keycloak-push-mfa-extension

Keycloak Push MFA Extension

Maven Central

A Keycloak extension that adds push-based multi-factor authentication, similar to passkey primitives.

Quick Start

Using Maven (recommended)

Add the dependency to your project:

<dependency>
    <groupId>de.arbeitsagentur.opdt</groupId>
    <artifactId>keycloak-push-mfa-extension</artifactId>
    <!-- Check the badge above or Maven Central for the latest version -->
    <version>1.5.0</version>
</dependency>

Copy the JAR to Keycloak's providers/ directory and restart Keycloak.

Building from Source

# Build the provider
mvn -DskipTests package

# Run Keycloak with the demo realm
docker compose up
  • Keycloak Admin Console: http://localhost:8080 (login: admin / admin)
  • Demo Realm: demo with test user test / test
  • Demo Configuration: See config/demo-realm.json for a working example

Introduction

This project extends Keycloak with a push-style second factor that mimics passkey primitives. After initial enrollment, the mobile app never receives the real user identifier from Keycloak; instead, it works with a credential id that only the app can map back to the real user. Everything is implemented with standard Keycloak SPIs plus a small JAX-RS resource exposed under /realms/<realm>/push-mfa.

High Level Flow

sequenceDiagram
    autonumber
    participant Browser as User / Browser
    participant Keycloak as Keycloak Server
    participant Provider as Push Provider (FCM/APNs)
    participant Mobile as Mobile App

    Note over Browser, Mobile: **Phase 1: Enrollment (Register Push MFA Device)**

    Browser->>Keycloak: Login & Trigger Enrollment
    Keycloak-->>Browser: Render QR Code & Start SSE Listener

    par Parallel Actions
        Browser->>Keycloak: SSE Request (Read Current Status)
        Browser->>Mobile: Scan QR Code
    end

    Note over Mobile: Verify Token & Generate User Key Pair

    Mobile->>Keycloak: POST /enroll/complete
    Note right of Mobile: Payload: Device JWT + Public JWK<br/>Signed with new Device Private Key

    Keycloak->>Keycloak: Verify Signature & Store Device Credential
    Keycloak-->>Browser: SSE Event: { status: "APPROVED" }
    Browser->>Keycloak: Auto-Submit Form (Enrollment Complete)

    Note over Browser, Mobile: **Phase 2: Login (Push MFA Confirmation)**

    Browser->>Keycloak: Login (Username/Password)
    Keycloak->>Keycloak: Generate Challenge & ConfirmToken

    par Parallel Actions
        Keycloak-->>Browser: Render "Waiting for approval..." Page
        Browser->>Keycloak: SSE Request (Read Current Challenge Status)
        Keycloak->>Provider: Send Push Notification
        Note right of Keycloak: Payload: ConfirmToken<br/>(Credential ID, ChallengeID)
    end

    Provider->>Mobile: Deliver Push Notification

    Mobile->>Mobile: Decrypt Token & Resolve User ID
    Mobile-->>Browser: (User Prompt: Approve?)
    Browser-->>Mobile: User Taps "Approve"

    Mobile->>Keycloak: POST /login/challenges/{cid}/respond
    Note right of Mobile: Payload: LoginToken (Action: Approve)<br/>Auth: DPoP Header + Access Token<br/>Signed with Device Private Key

    Keycloak->>Keycloak: Verify DPoP, Signature & Challenge ID
    Keycloak-->>Browser: SSE Event: { status: "APPROVED" }
    Browser->>Keycloak: Auto-Submit Form (Login Success)
Loading

The SSE endpoints keep a long-lived stream open per browser, but each Keycloak node uses a single node-local poller thread to watch the shared challenge store for all of its currently connected SSE clients. Cross-node delivery works because every node reads the same challenge state from shared storage; if a node dies, the browser's normal EventSource reconnect can land on another node and that node becomes responsible for the stream.

Documentation

Document Description
Setup Guide Step-by-step configuration instructions and Keycloak concepts
Flow Details Technical details of enrollment, login, SSE, and DPoP authentication
API Reference REST endpoints for mobile apps
Configuration All configuration options reference
App Implementation Guide for mobile app developers
SPI Reference Push notification, event, and rate limiting SPIs
UI Customization Theme and template customization
Security Security model and mobile app obligations
Mobile Mock Testing without a real mobile app
Troubleshooting Common issues and solutions

About

Keycloak push MFA Extension

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors