2FA bypass of Webauthn-only accounts


I’ve had a search around here and can’t find anything which seems to address this problem. We’ve recently installed Keycloak (v18) to secure some of our systems, and we’ve discovered an interesting wrinkle in the 2FA setup.

We (broadly) have three classes of user: those with YubiKey Webauthn generators, those with Google Authenticator (or similar) time-based OTP (TOTP) generators, and those with both.

For those with both or TOTP only, the option to use it or the YubiKey is presented to login. For those without TOTP, the option to use the YubiKey is not presented, and the user can login with just the username and password, bypassing all the 2FA.

This has the unfortunate side-effect that any attacker with a username and password can login and add their own 2FA method, and then obtain access to any other system using it, which drives a coach and horses through the whole thing.

Are we missing something somewhere?


Can you show us your used Authentication Flow? Most probably a bit of optimization there would do the trick.

In all honesty, probably not. But I can demonstrate it simply enough with the default docker image:

ct-lt-2205:/home/dickon/ (0) 5 $ docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:21.1.1 start-dev

Login admin/admin as usual. Create two users. On the first, set a password and an action to setup OTP. On the second, set a password and an action to setup Webauthn. Logout.

Login as the first, setup Google Authenticator or the TOTP generator of your choice. Logout. Login again, and you’ll be prompted for the OTP code.

Login as the second, setup a Webauthn token as prompted. Logout. Login again, and you’ll be straight through with no 2FA prompt.

This feels very wrong, and I’m assuming it’s a bug somewhere, but I don’t know enough about Keycloak to diagnose it. I’m actually trying to fix this on behalf of our Ops team, as I’m between projects ATM, and some systems admin experience; large Java applications are not my area of expertise, however…

Any help gratefully received. Ta.

The default browser authentication flow doesn’t have any WebAuthN check configured. That’s why it isn’t happen.
So, my assumption to tweek and adjust the authentication flow is most probably still the way to go. No bug involved.

OK. Assuming the default browser flow, how would I go about adding it in?

You have to copy the default browser flow and adjust it accordingly to your requirements. The default built-in flows are not editable.
See documentation here: Server Administration Guide

Based on that what I understood from your previous posts, I’ve created an example, which might fit:

I left the condition, as you want one of both MFA options as a required step. OTP and WebAuthN must be alternative in a common (required) subflow, so that you can choose either one of them (if a user has configured both, OTP and WebAuthN).
Hopefully this example works for you, perhaps you have to make some more adjustments.

And… don’t forget to bind your custom flow to be used as the browser flow!