Overview

Passkey autofill provides an incredibly seamless and secure way for users to sign-in using their passkey.

In the previous guide, we demonstrated how to allow users to enroll a passkey using the pre-built UI.

In this guide, we will show you how to add passkey autofill to your Keycloak sign-in flow using Authsignal. Users will be able to sign-in by simply clicking the input and authenticating with the passkey that they enrolled with the prebuilt UI.

Passkey autofill.

Code example

If you haven’t already added our provider to your Keycloak instance, see our guide on adding the Authsignal Keycloak provider for instructions.

You can find the Keycloak provider code example referenced in this guide on GitHub. For advanced situations, you can modify the provider to meet your specific requirements. However, this provider will work for most use cases.

Authsignal configuration

Setup a custom domain

In the previous guide, we setup a custom domain on Authsignal. If you haven’t done this already, see our guide on setting up a custom domain for instructions.

Local development

To make things easy, we will make a few simple changes to your local development setup so that you can simulate a production environment, locally. We will use mkcert to create a local SSL certificate and local-ssl-proxy to proxy requests to your local Keycloak server.

Replace keycloak-demo.authsignallabs.com with your domain. If using the prebuilt UI, this will differ from your Authsignal custom domain. i.e. keycloak-demo.authsignallabs.com which our demo app uses vs keycloak-demo-auth.authsignallabs.com where the prebuilt UI is running - in this demonstration.

1. SSL Certificate Setup

brew install mkcert
mkcert -install
mkcert your_domain # e.g. mkcert keycloak-demo.authsignallabs.com

Take note of the path to the .pem files that are created as you’ll need them in the next steps.

2. Custom Domain to Local IP Address Mapping

Map your custom domain to your local IP address by adding the following line to your hosts file:

sudo vim /etc/hosts

Add this line:

127.0.0.1 your_domain # e.g. 127.0.0.1 keycloak-demo.authsignallabs.com

3. Run the proxy server

npx local-ssl-proxy --key keycloak-demo.authsignallabs.com-key.pem --cert keycloak-demo.authsignallabs.com.pem --source 8444 --target 8443

4. Run your Keycloak server

Learn more about the Keycloak server configuration options.

bin/kc.sh start-dev \
--hostname=keycloak-demo.authsignallabs.com \
--https-certificate-file=keycloak-demo.authsignallabs.com.pem \
--https-certificate-key-file=keycloak-demo.authsignallabs.com-key.pem \
--http-host=127.0.0.1

5. Sign in to your Keycloak server

Open your browser and navigate to https://your_domain:8443 (e.g. https://keycloak-demo.authsignallabs.com:8443) which will redirect you to the admin sign-in page on your proxied https Keycloak server.

Opening the Keycloak admin sign-in page

Changes to your Keycloak configuration

In your Keycloak admin UI, navigate to the Authentication section and click on your custom flow. Delete the default username and password flow.

Delete the default username and password flow

Click on the Authsignal Authenticator settings and toggle on Enable Passkey Autofill.

Enable Passkey Autofill

Enable Passkey Autofill

Sign-in UI configuration

We need to add some custom code to the Keycloak sign-in page so that it can use Authsignal’s Browser SDK to handle passkey autofill.

Start by creating a new theme for your Keycloak instance, at the following path: themes/mytheme/login/login.ftl on your keycloak server.

You can call the theme whatever you want, but for this example, we will call it mytheme.

Add the following code to the login.ftl file. This code will allow autofill to work when the user clicks the input.

themes/mytheme/login/login.ftl

<link rel="stylesheet" href="${url.resourcesPath}/css/styles.css">
<script src="${url.resourcesPath}/js/script.js"></script>
<div class="login-container">
  <div class="login-card">
    <div class="login-header">
      <p>Please enter your credentials to continue</p>
    </div>

    <form action="${url.loginAction}" method="post" class="login-form">
      <div class="form-group">
        <label for="username">Username</label>
        <input
          id="username" 
          name="username" 
          type="text" 
          autocomplete="username webauthn"
          placeholder="Enter your username"
        />
      </div>
      
      <div class="form-group">
        <label for="password">Password</label>
        <input 
          id="password" 
          name="password" 
          type="password"
          placeholder="Enter your password"
        />
      </div>

      <button type="submit">Sign In</button>
    </form>
  </div>
</div>

Create a Javascript file at themes/mytheme/login/resources/js/script.js and add the following code. This code is required to allow autofill to work when the user clicks the input.

themes/mytheme/login/resources/js/script.js
function setWebauthnAttribute() {
  var usernameInput = document.getElementById("username");
  var passwordInput = document.getElementById("password");

  const formElement = document.querySelector("form");

  if (usernameInput && passwordInput) {
    usernameInput.setAttribute("autocomplete", "username webauthn");

    // NOTE: Replace the following values with your Authsignal tenant ID and server URL
    var client = new window.authsignal.Authsignal({
      tenantId: "YOUR_TENANT_ID",
      baseUrl: "https://api.authsignal.com/v1",
    });

    client.passkey
      .signIn({ autofill: true })
      .then((response) => {
        if (response) {
          const hiddenTokenInput = document.createElement("input");
          hiddenTokenInput.type = "hidden";
          hiddenTokenInput.name = "token";
          hiddenTokenInput.value = response.token;
          formElement.appendChild(hiddenTokenInput);
          formElement.submit();
        }
      })
      .catch((error) => {
        console.log("error", error);
      });
  }
}

function loadAuthsignalSdk() {
  var script = document.createElement("script");
  script.onload = setWebauthnAttribute;
  script.src = "https://unpkg.com/@authsignal/browser@0.5.2/dist/index.min.js";
  document.head.appendChild(script);
}

document.addEventListener("DOMContentLoaded", loadAuthsignalSdk);

Restart your Keycloak server, and navigate to the Realm settings section in the Keycloak admin UI. Click Themes. On the Login theme line, select mytheme and click Save.

Opening the Keycloak admin sign-in page

You can style your theme by adding your own CSS to the themes/mytheme/login/resources/css/styles.css file.

Navigate to your realm’s sign-in page and you should see the new theme.

You can find your realm’s sign in page URL by selecting your realm and then navigating to the Clients section in the Keycloak admin UI and clicking Home URL.

Find your realm's Home URL

Passkeys that have been enrolled using the Authsignal prebuilt UI will be available for autofill when both:

  1. Your Keycloak login page is hosted on the same domain or subdomain as where the passkeys were enrolled.
  2. The relying party ID (RP ID) matches the domain where the passkeys were enrolled

In this demo we enrolled passkeys on the keycloak-demo-auth.authsignallabs.com domain, and have an RP ID of authsignallabs.com, so we can autofill passkeys on the keycloak-demo.authsignallabs.com domain.

This is due to the security model of WebAuthn/passkeys, which restricts passkey usage to the domain (or its subdomains) where they were originally registered.

Passkey autofill.

Conclusion

That’s it! You’ve successfully added passkey autofill to your Keycloak sign-in flow using Authsignal.