Keycloak MFA Plugins

repository·main·Indexed 43 days ago

https://github.com/netzbegruenung/keycloak-mfa-plugins

A collection of multi-factor authentication extensions for Keycloak, featuring an App Authenticator for cryptographic challenge-response 2FA and an Email Authenticator plugin. Includes documentation for the App Authenticator API, signature requirements using JWTs and HTTP Message Signatures, and integration guides for Flutter via the flutter-keycloak-authenticator package.

Tokens
8.2K
Snippets
16
Records
34
Agent score
55%

What's inside Keycloak MFA Plugins

  1. Overview of App Authenticator for Keycloak

    main

    App Authenticator is a Keycloak extension designed to improve the two-factor authentication (2FA) experience. Instead of manually entering Time-based One-Time Password (TOTP) codes, users can approve or reject login attempts directly via a mobile authenticator app.

    Security is enhanced by using a cryptographic keypair generated on the user's device. The client uses this keypair to sign authentication challenges, ensuring the authenticity of the user during the login process.

  2. How the MFA Dev Runner works

    main

    The mfa-dev-runner is a development utility that leverages the Keycloak Quarkus extension (keycloak-quarkus-server). It does not contain its own source code but acts as an orchestrator.

    Its configuration includes:

    1. Dependencies: It pulls in sms-authenticator, email-authenticator, app-authenticator, and enforce-mfa.
    2. Runtime: It uses the quarkus-maven-plugin to launch Keycloak in development mode.
    3. Workflow: It automates the process of building and providing plugin JARs to the Keycloak providers folder, enabling hot-reloading and easy debugging of MFA authenticators.
  3. How to sign requests using Signature Tokens

    main

    The App Authenticator API requires requests to be signed using client-side generated JSON Web Tokens (JWTs) to ensure authenticity. Instead of standard HTTP Message Signatures, this implementation uses a JWT as a request signature, which must be transmitted in the x-signature header.

    Signature Requirements

    1. Header & Algorithm

    • The JWT must be signed using an asymmetric algorithm.
    • The kid (Key ID) claim must be set to the authenticator id.
    • Supported algorithms:
      • PS512 (using an RSASSA-PSS asymmetric key)
      • ES512 (using an EC asymmetric key)

    2. JWT Payload Claims

    • sub: The user ID (extracted from the sub claim of the Keycloak action token issued via the activation token URL).
    • exp: An expiration time of approximately 30 seconds to prevent replay attacks.
    • jti: A unique UUID for the JWT to enable one-time token usage. The server tracks this ID until expiration.
    • typ: A value representing the corresponding endpoint or action (e.g., get-challenges).
    • Additional Parameters: Any other request parameters that require signing must be included in the payload.
  4. Understand the Activation Token URL structure

    main

    The Activation Token URL is provided to the authenticator app via a QR code or copy-paste option in the Keycloak My Account Console. It is used to initiate the setup process.

    URL Parameters:

    • tab_id: The Tab Id of the user's browser session.
    • client_id: The Client ID the user is logging in with (e.g., account-console).
    • key: A Keycloak Action Token formatted as a JWT. This token contains claims like iss (issuer), sub (subject/user ID), and typ (token type, e.g., app-setup-action-token).
    http://192.168.2.127:8080/realms/dev/login-actions/action-token?key=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxYWEzY2FhMS00ZmEwLTQzNTUtYWE1ZC1lZTVhNzc4OTA0NGYifQ.eyJleHAiOjE3MDE0Mjg0NjQsImlhdCI6MTcwMTQyODE2NCwianRpIjoiZTlhZWEyYzQtOWM4Ny00MjBkLTg2NjctNjg0YzA5MjM0ZTA3IiwiaXNzIjoiaHR0cDovLzE5Mi4xNjguMi4xMjc6ODA4MC9yZWFsbXMvZGV2IiwiYXVkIjoiaHR0cDovLzE5Mi4xNjguMi4xMjc6ODA4MC9yZWFsbXMvZGV2Iiwic3ViIjoiYjViZjYzOTYtMjFjZC00NjZjLTk2MzMtMGRlM2ExNTJiYTYzIiwidHlwIjoiYXBwLXNldHVwLWFjdGlvbi10b2tlbiIsIm5vbmNlIjoiZTlhZWEyYzQtOWM4Ny00MjBkLTg2NjctNjg0YzA5MjM0ZTA3Iiwib2FzaWQiOiIwYTE4YTM0MS01ZWE3LTRlZjgtYmRjNS1kZTdmNDY5MjcyYjEuUldfTERIazV6QjguMjk1OTViZmEtNzU4ZS00MjFiLWE4ZDMtMGFmYjQ4ZDE3MjhkIn0.XLh09uLq9Ybx_fCIcMhWcNELy9wnPaGMZ8pRusJ_b_g&client_id=account-console&tab_id=RW_LDHk5zB8
  5. How the App Authenticator flow works

    main

    The authentication process follows a five-step lifecycle involving token exchange, key registration, challenge-response, and verification:

    1. Activation Token Exchange: Keycloak generates an Activation Token. The user provides this to the client app (via QR code scanning or manual entry).
    2. Key Pair Generation: The user's device generates a cryptographic keypair and stores the private key in secure storage. The client sends the public key and a unique authenticator id (uuid) to Keycloak to associate the device with the user account.
    3. Challenge Transmission: When a login is initiated, Keycloak sends a challenge to the device. This is handled either via push notifications or by the client polling a specific Keycloak endpoint.
    4. Challenge Signing: The device signs the received challenge using its locally stored private key and sends the signature back to Keycloak.
    5. Verification Process: Keycloak verifies the signature against the registered public key. Based on the validity of the signature, Keycloak accepts or rejects the login attempt.
  6. How users set up and use SMS 2FA

    main

    User Setup

    Once configured, users can set up SMS authentication via the Account Console at /realms/realm/account/#/account-security/signing-in by entering and confirming their phone number.

    Enforcing SMS 2FA

    To force users to use SMS 2FA, enable the Force 2FA option in the SMS Authenticator configuration. If enabled, users who do not have another 2FA method active will be required to set up the SMS Authenticator.

  7. How Trusted Device works and how to revoke them

    main

    The Trusted Device plugin works by storing a signed cookie and a matching netzbegruenung-trusted-device credential after a user successfully completes a second-factor login and chooses to trust the device.

    • Automatic Bypass: As long as the signed cookie and the netzbegruenung-trusted-device credential are present and unexpired, the Trusted Device step succeeds, and the second-factor sub-flow is skipped.
    • Revocation: Users can view and revoke their trusted devices through the Keycloak account console at: /realms/realm/account/#/account-security/signing-in.
  8. Authenticate requests using the Signature HTTP Header

    main

    Requests are authenticated using the HTTP Message Signatures standard. The Signature header is a comma-separated string of key-value pairs containing a signature created by the client's private key.

    Header Components:

    • keyId: The device ID (authenticator ID) used to identify the key on the server.
    • created: A timestamp of signature creation (prevents replay attacks).
    • secret (optional): A challenge value. Must be used when confirming login attempts (e.g., Reply Challenge Endpoint) but must not be present when fetching challenges.
    • granted (optional): A boolean indicating if the login attempt was granted. Must be used when confirming login attempts but must not be present when fetching challenges.
    • signature: The Base64-encoded signature of the concatenated values.
    Signature: keyId:device_id,created:1763118313156,secret:J2XISxpDgzyPl5AuFbDzWKNIqx2tYVYNzOu5mHa7JbSCNLW1tQ9fiSPUtvdefiaGJMkxPhAGbE999d5iOMMnYIvNEJdkLpdf5fS3x6sFjBou7v8R2aiPteL0TvVBwMKCI1T5JGQAbJpszpkDRV945tx1iMW230yMdIf3iLexiw4AuTDfQEwC1MaslwDF5FoYCUBTlBGbr3MrgOowNJHyOxhsxmSB7AO4Ji3aDbPEIwg2YMWgPI2azpvtfZdT76reZgrqNJgAwIWgaDDdONhVOF55PyCeaYadqbQUQtWIMW9GHTKN3tWs7QCOUhORpnsiFDhL05jg7tNScZI3m9KFw3anc2yQgU78ZyecTYjQh131nKlbr7tt5Gvdz6WRZZNb4ZgfllpvVnSD1o713bIFDC1IMbXD5qJGMumliDiDwFLJiRmeSSOeOkE14waXI30bnRHteUcOqx706k4Svp4eYPq7jbBNCrwa4BWMhKd3SdhSAtrvyZJiO,granted:false,signature:J+1xejRxKm+Lq7awBW5BZE4CJ6M+udHKHq4xSN3WjU3bHAz6S5aH0AW071ZUvKi7lzCQujqtI05c+3z7eDjLDA==
  9. Configure the Trusted Device Authentication Flow

    main

    To use the plugin, you must enable the required action and modify your authentication flow. Since built-in flows are read-only, you must first duplicate the flow you intend to modify (e.g., the browser flow).

    1. Enable Required Action

    Navigate to /admin/master/console/#/YOUR-REALM/authentication/required-actions and enable Manage Trusted Device.

    2. Modify Authentication Flow

    In your duplicated flow, configure the 2FA section to use the Trusted Device check as an alternative to your existing second factor:

    1. Add Trusted Device and set it to Alternative.
    2. Add a sub-flow (set to Alternative) containing:
      • Your existing second-factor step(s) (e.g., OTP Form) set to Required.
      • Trusted Device Register set to Required.
  10. Configure the SMS Authenticator in Keycloak

    main

    After installation, you must configure the authentication flow and required actions in the Keycloak Admin Console:

    1. Enable Required Actions

    Navigate to /admin/master/console/#/realm/authentication/required-actions and enable:

    • Phone Validation
    • Update Mobile Number

    2. Update Authentication Flow

    1. Navigate to your realm's authentication settings: https://<your-keycloak-domain>/admin/master/console/#/YOUR-REALM/authentication.
    2. Edit the Browser flow.
    3. Add a new step next to the OTP Form step.
    4. Select SMS Authentication (2FA) and set its requirement to Alternative.
    5. Crucial: Set the Alias to sms-2fa. This specific alias is required for the confirmation SMS during phone number setup.

    3. Configure SMS Provider API

    In the execution configuration, set up the HTTP POST request details for your SMS provider using the following options:

  11. Access the H2 development database via Shell CLI

    main

    The development server uses an H2 file-based database located at h2db/mfa. While the Keycloak server is running, you can connect to it using the H2 command-line shell to inspect data.

    The following command automatically detects the correct H2 driver version from your project's pom.xml and launches the shell with the necessary connection string (jdbc:h2:file:./h2db/mfa;AUTO_SERVER=TRUE) and credentials (sa/password).

    H2_VERSION=$(mvn -f pom.xml dependency:tree -Dincludes=com.h2database:h2 -Dverbose | grep 'com.h2database:h2:jar:' | head -n 1 | cut -d: -f4)
    java -cp ~/.m2/repository/com/h2database/h2/$H2_VERSION/h2-$H2_VERSION.jar org.h2.tools.Shell -url "jdbc:h2:file:./h2db/mfa;AUTO_SERVER=TRUE" -user sa -password password