Keycloak is an open source identity broker that allows you to combine user credentials from different providers (such as Google OAuth, LDAP, GitLab, etc.) as well as locally-stored credentials into a single authentication provider that can integrate with downstream applications using either SAML2.0 or OpenID Connect. I use it in my home lab as a single sign on provider using local accounts (I gave a talk about my setup at BSides Orlando 2020, check it out if you’re interested!).
My setup leverages local user accounts with WebAuthn for password-less authentication. Users log in by providing their user name, then are prompted by their browser to authenticate using their device’s platform authenticator (a built-in security key created using the device’s hardware platform). The authentication process requires a biometric or PIN, then provides cryptographic proof to the web application (in this case, Keycloak) that the user is who they say they are, complete with an attestation chain proving the authenticator is legitimate, and that the keys are stored in a hardware-backed non-exportable store. This is an excellent user experience that provides MFA without making users bother with passwords or authenticator apps. Keycloak supports this experience, and it works great with Windows and Android devices. However, Apple’s implementation (used in Safari on MacOS and iOS) has two quirks that make it incompatible with Keycloak as of this writing.
The Safari (or rather, WebKit, the underlying browser engine) WebAuthn quirks are deliberate, and detailed in their announcement blog post for the feature:
Unfortunately, those quirks both effect Keycloak’s WebAuthn implementation:
I’ve filed bugs (free account required to view) about this to the Keycloak issue tracker, but in the mean time both of these problems can be worked around.
Keycloak provides a robust custom theme system that allows you to customize pretty much any part of the user-facing parts of the application, including the various login flow subpages. First, setup Keycloak to use WebAuthn for first and second factor authentication using their guide, then come back to this post.
We’ll use a custom theme that modifies the login page to require a “gesture” (specifically, tapping/clicking on a button) to trigger the WebAuthn API, which will satisfy the WebKit anti-abuse measures with as few changes as possible from the default theme. You can find the theme files on my GitHub. Download them, then put the touchid-fix
folder into the Keycloak themes/
directory. Then restart Keycloak, and in the admin console change the default login theme to the touchid-fix
theme. Instead of it prompting for WebAuthn credentials on load, you’ll see a button to press to login instead. It also adds a button to manually report a login failure, which makes it easier to switch to your backup login method if you’re on a device that doesn’t have WebAuthn setup.
This isn’t the only thing you’ll have to do to make it compatible though. If you leave it here, you’ll notice that every authentication attempt after the first fails, which is caused by the counter verification thinking you have a cloned authenticator (since the Apple authenticators never increment the counter).
While the previous workaround uses intended functionality to customize Keycloak, this second workaround… is a bit of a hack. Basically, when you login with an authenticator that reports a counter value of n, Keycloak stores the value n+1; the next time you authenticate, it errors if your new counter value is less than n+1. We’re going to work around this by using DBMS triggers to reset the counter to zero whenever Keycloak updates it.
This is a hack. It is not supported functionality, nor is it guaranteed to continue working in the future. It could cause your Keycloak instance to stop working, or introduce a security weakness (beyond the fact that you’re disabling a security mechanism explicitly). While I’ve been running my install like this for a while with no negative effects as far as I can tell, I make no warranty whatsoever that this won’t do something horrible to your install, or cause demons to spew forth from your server. Proceed at your own risk.
You’ll need to use a separate database for Keycloak rather than its built-in one. An example docker-compose
file to do this can be found in the Keycloak Containers repo. Note that the example file uses a very old version of MySQL that you will have to upgrade for this to work. I use the mysql:latest
image, which is bad practice in production but fine for a lab. You need at least version 8.2. You can export your realm, switch to using an external database, then re-import it if you already have users or clients configured. You could also use a different RDBMS, but you’ll have to adapt the trigger.
Once you’ve got Keycloak running using a MySQL database, you’ll need to login to the database yourself using the MySQL client. If you use the compose file from above, it’s [sudo] docker-compose exec mysql bash
, then log in normally using the mysql
binary with your password. Once you’re at the prompt, select the Keycloak MySQL database (use keycloak;
) and then run the following commands:
|
|
This creates a trigger that intercepts writes to the table and replaces the counter value with zero before committing the data. The counter value will be read back on authentication, which since it’s zero will always succeed the duplication check.
And that’s it. You can now register and use Apple platform authenticators in Keycloak! You also need to set the WebAuthn policy to the appropriate values (ES256 signatures allowed, indirect or no attestation, platform authenticator, user verification not disallowed).