Using Howdy on Linux like Windows Hello
and falling back to password when the webcam is disconnected

I use a Logitech Brio webcam, which has an infrared camera and works quite nicely with Windows Hello.
On Linux, the closest equivalent I have found is Howdy, which lets you use face authentication via PAM. So basically I wanted the same workflow on Linux:
- camera connected → try face unlock
- face recognized → authenticate immediately
- camera not connected → do not get stuck, just ask for password
- face auth fails → fall back to password
The last part is where things got slightly annoying.
The setup
I had Howdy working for terminal sudo use. My PAM config had Howdy in the auth stack, and for CLI this mostly behaved fine.
The kind of entry I started with was roughly like this in common-auth:
auth [success=3 default=ignore] /usr/lib/security/howdy/pam_howdy.so
auth [success=2 default=ignore] pam_unix.so nullok try_first_pass
auth [success=1 default=ignore] pam_sss.so use_first_pass
auth requisite pam_deny.so
auth required pam_permit.so
auth optional pam_cap.so
The intent here is simple enough: try Howdy first, and if it works jump ahead. Otherwise continue to the normal password auth.
But GUI authentication was not behaving as nicely.
If the Brio was connected, Howdy worked.
If the Brio was disconnected, GUI auth got stuck in a Howdy loop instead of cleanly falling back to password.
First thing: do not keep Howdy in common-auth
The shared common-auth stack is used by a bunch of different PAM services. Putting Howdy there means every service that includes common-auth gets Howdy implicitly.
That can work, but it also makes debugging painful, especially for GUI flows.
So I commented Howdy out from /etc/pam.d/common-auth.
# auth [success=3 default=ignore] /usr/lib/security/howdy/pam_howdy.so
Then I added Howdy explicitly only to the PAM services where I wanted it.
For CLI:
/etc/pam.d/sudo
/etc/pam.d/sudo-i
For GDM login:
/etc/pam.d/gdm-password
For GUI admin prompts via pkexec:
/etc/pam.d/polkit-1
Finding out what GUI auth is actually using
I initially assumed GUI auth would hit gdm-password, but that was not the case for all GUI prompts.
To check what was happening, I watched the journal while triggering the GUI auth prompt:
sudo journalctl -f | grep -i pam
For pkexec true, I saw something like:
pkexec[54732]: pam_unix(polkit-1:session): session opened for user root(uid=0)
So even though I did not have /etc/pam.d/polkit-1 initially, PAM was still using polkit-1 as the service name.
That means pkexec / Polkit GUI authentication wanted:
/etc/pam.d/polkit-1
So I created that file.
sudo nano /etc/pam.d/polkit-1
with this base content:
#%PAM-1.0
@include common-auth
account include common-account
password include common-password
session include common-session
At this point, adding Howdy directly worked when the camera was connected.
auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
@include common-auth
But it still had the same problem when the camera was disconnected: the GUI prompt kept trying Howdy again and again.
The actual fix: skip Howdy before it runs if the camera is absent
The important bit is this: do not rely on Howdy failing correctly when the camera is disconnected.
Instead, check for the camera device first. If the camera is not present, skip the Howdy PAM line entirely and go straight to password auth.
I created a small script:
sudo nano /usr/local/bin/howdy-camera-present
with:
#!/bin/sh
CAMERA="/dev/video0"
[ -e "$CAMERA" ] || exit 1
[ -r "$CAMERA" ] || exit 1
exit 0
Then:
sudo chmod +x /usr/local/bin/howdy-camera-present
In my case, the camera path needs to match whatever Howdy is configured to use.
You can check that with:
grep -i "device_path" /lib/security/howdy/config.ini /etc/howdy/config.ini 2>/dev/null
If your Howdy config says something like:
device_path = /dev/video2
then update the script:
CAMERA="/dev/video2"
You can test the script directly:
/usr/local/bin/howdy-camera-present; echo $?
With the camera connected, it should print:
0
With the camera disconnected, it should print:
1
Final PAM config for Polkit GUI prompts
This is what I used for /etc/pam.d/polkit-1:
#%PAM-1.0
auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
@include common-auth
account include common-account
password include common-password
session include common-session
The important two lines are:
auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
This means:
- if the camera check succeeds, continue to Howdy
- if the camera check fails, skip the next line, which is Howdy
- then continue to normal password auth via
common-auth
So with the webcam connected:
pkexec → camera check passes → Howdy → auth succeeds
With the webcam disconnected:
pkexec → camera check fails → skip Howdy → password prompt
Final PAM config for GDM login
Same idea for /etc/pam.d/gdm-password.
Add these before @include common-auth:
auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
@include common-auth
So the top of the file looks roughly like:
#%PAM-1.0
auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
@include common-auth
Final PAM config for CLI sudo
For /etc/pam.d/sudo and /etc/pam.d/sudo-i, I used the same guard.
Instead of just:
auth sufficient /usr/lib/security/howdy/pam_howdy.so
use:
auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
auth sufficient /usr/lib/security/howdy/pam_howdy.so
The fallback password path is already handled by the rest of the sudo PAM file.
Checking where Howdy is still configured
To verify the final state:
cd /etc/pam.d
grep -R "howdy"
My final state looked roughly like:
sudo-i:auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
sudo-i:auth sufficient /usr/lib/security/howdy/pam_howdy.so
gdm-password:auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
gdm-password:auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
polkit-1:auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
polkit-1:auth [success=done default=ignore] /usr/lib/security/howdy/pam_howdy.so
sudo:auth [success=ignore default=1] pam_exec.so quiet /usr/local/bin/howdy-camera-present
sudo:auth sufficient /usr/lib/security/howdy/pam_howdy.so
common-auth:# auth [success=3 default=ignore] /usr/lib/security/howdy/pam_howdy.so
The key thing is that Howdy is commented out in common-auth.
Testing
For CLI:
sudo -k
sudo ls
For Polkit GUI auth:
pkexec true
For GDM, lock the session or log out and back in.
I would strongly recommend keeping a root shell open while changing PAM files. It is very easy to lock yourself out with a bad PAM config.
Final result
Now the behavior is what I wanted:
- Logitech Brio connected → Howdy face auth works
- Brio disconnected → password prompt comes up directly
- CLI sudo works
- GUI Polkit prompts work
- GDM login works
- Howdy is not globally injected into every auth flow via
common-auth
Anyway, documenting this mainly for myself because every time I touch PAM files I need to rediscover how the control flow works again.



