Swaylock + U2F: How to get working fallback?

Howdy. I suspect this isn’t strictly NixOS related, but I’m guessing I’ll have just as good of a chance of finding someone who has a good answer here as anywhere.

I’ve set up PAM to support biometric authentication via a FIDO2 key for Swaylock. It looks like this:

{ config, ... }: {
  config =
    security.pam.u2f = {
      authFile = config.sops.secrets."u2f/authMapping".path;
    };
    security.pam.services.swaylock = {
      u2fAuth = true;
      rules.auth.u2f.args = lib.mkAfter [
        "pinverification=0"
        "userverification=1"
      ];
    };
  };
}

To be clear, this works. I can in fact hit enter on Swaylock and perform biometric auth using a FIDO2 key. I can verify that if I fail biometric authentication, it indeed doesn’t unlock. So far so good.

However, while I want this to be sufficient for locking the screen (at least for now), I’d like for it to still be possible to enter a password. Unfortunately, for some reason, swaylock doesn’t seem to have a good way to “fall through” to other authentication: once there is a PAM failure in pam_u2f, the entire authentication flow restarts. The only way I can bypass pam_u2f is by pulling the FIDO2 key entirely, at which point password authentication works completely like it normally does.

So I guess basically I’m looking for a workaround or solution here. Maybe I can somehow have another sufficient pass of pam_unix before the U2F auth just for swaylock PAM, so that it ‘eats’ the password if it happens to be entered and correct, but doesn’t fail early if it’s empty?

What I’d really like to do eventually is just write my own screenlocker, at which point I’d be forced to figure out how to properly integrate PAM. But right now, I don’t even know enough about PAM to make odds and ends of this problem, so I’d really appreciate any insight. I am actually feeling some deja vu here; I think I may have had a similar issue at a corporation where we had PAM for security keys, except with i3lock. That said, I, unfortunately, do not remember the workaround :frowning: