All writing

Touch ID for sudo on macOS Sonoma and later, in tmux and IDE terminals

Touch ID for sudo that survives macOS updates is one pam_tid.so line in /etc/pam.d/sudo_local. Add pam_reattach above it for tmux and IDE terminals.

Since macOS 14 Sonoma, /etc/pam.d/sudo includes a file called sudo_local, and that file survives system updates. Put auth sufficient pam_tid.so in it and you have Touch ID for sudo: sudo asks for your fingerprint instead of your password. Inside tmux, screen or some IDE terminals you also need pam_reattach, ordered above that line, because those processes are detached from the session that can draw the Touch ID prompt.

The rest of this post is the walkthrough: what the files contain, the manual edit, why tmux misses the prompt, where you will still type your password, and how to undo all of it. If you would rather run one command, the studio's MIT-licensed Touch ID for sudo makes the same edit, with a backup and an undo.

What macOS ships in /etc/pam.d

sudo authenticates through PAM, and its policy lives in /etc/pam.d/sudo. On the Mac this was written on, running macOS 27.0.1, that file reads:

# sudo: auth account password session
auth       include        sudo_local
auth       sufficient     pam_smartcard.so
auth       required       pam_opendirectory.so
account    required       pam_permit.so
password   required       pam_deny.so
session    required       pam_permit.so

The first auth line pulls in a second policy, sudo_local, before the smart-card and password checks run. The include form, per man 5 pam.conf, "causes entries from a different chain … to be included in the current one". Apple does not ship sudo_local itself. It ships /etc/pam.d/sudo_local.template, which on the same Mac reads:

# sudo_local: local config file which survives system update and is included for sudo
# uncomment following line to enable Touch ID for sudo
#auth       sufficient     pam_tid.so

That first comment is the whole point. Before Sonoma the usual advice was to add pam_tid.so to /etc/pam.d/sudo directly, and every macOS update replaced that file and quietly removed the line. Six Colors noted the change in August 2023, during the Sonoma betas.

You can check the survival claim on your own machine. Here, every Apple-managed file in /etc/pam.d carries the timestamp of the last system update, 24 September, while sudo_local still carries 15 August, the day it was written:

ls -l /etc/pam.d/

pam_tid.so is the Touch ID module. It lives at /usr/lib/pam/pam_tid.so.2, and Apple ships no man page for it (man pam_tid returns "No manual entry"), so what it does has to be read off its behaviour and the PAM control flags around it.

The manual method

If your /etc/pam.d/sudo has the auth include sudo_local line, three steps do it. First, keep a root shell open in a second terminal window while you work. It costs nothing, and it is the way back if you break the file:

sudo -s

Then, in your first window, write sudo_local with the template's line uncommented:

sudo tee /etc/pam.d/sudo_local > /dev/null <<'EOF'
# sudo_local: local config file which survives system update and is included for sudo
auth       sufficient     pam_tid.so
EOF

Six Colors' version copies the template and uncomments the line in an editor, which ends in the same file:

cd /etc/pam.d
sudo cp sudo_local.template sudo_local
sudo pico sudo_local

Finally, clear your cached credentials and try it. sudo -k "invalidates the user's cached credentials for the current session", per man sudo, so the next sudo has to authenticate:

sudo -k
sudo true

You should see the system dialog asking for Touch ID, with a "Use Password" button underneath.

What sufficient means

man 5 pam.conf defines it: "If this module succeeds, the chain is broken and the result is success. If it fails, the rest of the chain still runs." A matching fingerprint ends authentication there. A cancelled prompt, a failed match, or Touch ID being unavailable lets the chain carry on to pam_opendirectory.so, which is the ordinary password check. The fingerprint is an alternative to the password, never a replacement for it.

fingerprint matches

cancelled, no match, unavailable

sudo

sudo_local: pam_reattach (optional)

sudo_local: pam_tid (sufficient)

authenticated

pam_smartcard

pam_opendirectory: your password

Why tmux, screen and IDE terminals ask for the password

Set up as above, sudo in Terminal shows the prompt, and the same sudo inside tmux asks for your password. Nothing is broken. The pam_reattach README explains it: services tied to the GUI, "such as pasteboard and Touch ID", are "strictly tied to the login session of a user and as such unavailable for programs in the background session". A tmux server is exactly such a background program, built to outlive the terminal that started it, so a sudo running under it has no route to the session that draws the dialog. pam_tid cannot prompt, fails, and sufficient lets the chain fall through to the password.

A split terminal with the fingerprint left outside and its cable unplugged

pam_reattach is a PAM module that "will attempt to move the current program (e.g. sudo) to the current active login session, after which the remaining PAM modules will have access to the per-session services like Touch ID". It has to run before pam_tid. Its author suggests marking it optional "to prevent getting locked out in case of bugs".

The same applies to editors that run sudo in an embedded terminal. The Touch ID for sudo README puts it carefully: VS Code and JetBrains IDEs "run sudo in an embedded terminal that may not be attached to your GUI session", and pam_reattach fixes that case too.

iTerm2 has its own switch

iTerm2 can host your shells in a separate long-lived process so sessions survive a logout. Its source names the trade-off directly: the Advanced setting "Allow sessions to survive logging out and back in" is on by default, and its description says "This breaks the 'auth sufficient pam_tid.so' hack some people use to allow sudo to authenticate with Touch ID." Either turn it off in Settings, Advanced, or keep it and add pam_reattach, which addresses the same detachment.

Adding pam_reattach

Install it with Homebrew (formula page, MIT licensed) or MacPorts:

brew install pam-reattach
sudo port install pam-reattach

Homebrew on Apple silicon puts the module under /opt/homebrew, which PAM does not search, so the README says to give the full path. The sudo_local then reads:

# sudo_local: local config file which survives system update and is included for sudo
auth       optional       /opt/homebrew/lib/pam/pam_reattach.so
auth       sufficient     pam_tid.so

On an Intel Mac with Homebrew the module is at /usr/local/lib/pam/pam_reattach.so, and MacPorts installs it to /opt/local/lib/pam/pam_reattach.so. Check which one you have:

ls /opt/homebrew/lib/pam/ /usr/local/lib/pam/ /opt/local/lib/pam/ 2>/dev/null

Two details matter. The order: pam_reattach above pam_tid, or the reattach happens after the prompt has already failed. And optional: if a Homebrew upgrade ever moves or breaks the module, sudo degrades to your password instead of refusing you. Open a new tmux pane and run sudo -k; sudo true to test.

From the studio: Touch ID for sudo gets both details right for you: if it finds pam_reattach.so, it writes it as optional above pam_tid.so, with the full path.

The ignore_ssh option

pam_reattach also accepts ignore_ssh. Its man page says it detects probable SSH sessions by "the presence of $SSH_CLIENT, $SSH_CONNECTION, or $SSH_TTY in the environment" and becomes a no-op, which "helps prevent erroneously prompting for a touch when SSH and a terminal multiplexer like tmux(1) is in use". If you attach to the same tmux session both at the desk and over SSH, add it:

auth       optional       /opt/homebrew/lib/pam/pam_reattach.so ignore_ssh
auth       sufficient     pam_tid.so

Where you will still type your password

pam_tid is a convenience with a fallback, so knowing where it steps aside saves confusion:

  • Over SSH. The pam_reattach README notes that pam_tid "will try to avoid prompting for a touch when connected via SSH or another remote login method". You get the password prompt, as you should: the person at the keyboard may not be you.
  • With the lid closed, or on a Mac without a sensor. The sensor is unavailable, pam_tid fails, and sufficient passes you to the password.
  • With no fingerprint enrolled. bioutil -c prints the count for the current user. On this Mac it reports User 501: 1 biometric template(s). Zero means pam_tid has nothing to match.
  • Inside tmux, screen, an IDE terminal or iTerm2's persistent sessions, until pam_reattach or the iTerm2 setting above is in place.

Doing it with one command

Touch ID for sudo is the studio's MIT-licensed script for exactly this edit. It does nothing you could not do by hand with the steps above, and since it runs as root, here is everything it does, read from bin/touchid-sudo:

  • Preflight. It refuses to run unless /etc/pam.d/sudo contains the auth include sudo_local line, so it never edits /etc/pam.d/sudo itself. It checks that pam_tid.so exists and warns if bioutil -c reports no enrolled fingerprint.
  • Writes only /etc/pam.d/sudo_local, as root:wheel with mode 444. If pam_reattach.so is found under /opt/homebrew, /usr/local or /opt/local (checked in that order), it writes it as optional above pam_tid.so, with the full path. Lines in sudo_local that it did not write are preserved.
  • Backs up any existing file to /etc/pam.d/sudo_local.bak first.
  • Validates by running sudo -n -k true as you and searching the output for PAM errors. A healthy stack answers that a password is required; a broken one names the failure.
  • Rolls back on any sign of a PAM error, restoring the backup (or deleting the new file if there was none), and exits non-zero.
  • Is idempotent. A second run reports Already configured and changes nothing.
brew install abd3lraouf-studios/tap/macos-touchid-sudo
sudo touchid-sudo

touchid-sudo --status needs no root and prints the state, whether pam_reattach is active, and the file's contents. sudo touchid-sudo --disable removes the lines; if nothing else is left in sudo_local, it deletes the file, which is the factory state. It needs macOS 14 or later, for the same reason the manual method does.

Undoing it, and getting out of a broken file

By hand, deleting the file reverts sudo to password-only, because the include then has nothing to add:

A document and its backup copy, with an undo arrow and a life ring

sudo rm /etc/pam.d/sudo_local

If you wrote a broken sudo_local and closed every root shell, sudo may be unable to fix its own configuration. The way out is macOS Recovery: on Apple silicon, press and hold the power button until the startup options load, then choose Options; on an Intel Mac, hold Command-R at startup. Terminal is in the Utilities menu there. One detail trips people up: /etc is a symlink to /private/etc, and /private is listed in /usr/share/firmlinks, which means it lives on the data volume, not the read-only system volume. In Recovery, make sure the data volume is mounted in Disk Utility, then remove private/etc/pam.d/sudo_local from it.

The second terminal window from the manual method makes all of this unnecessary, which is why it comes first.

What it changes about security

Less than it seems in either direction. sudo still needs someone physically at the Mac with a matching finger, and it falls back to your password rather than around it. The real difference is that a sudo prompt no longer travels through your keyboard, which removes one place a keylogger could read it. Anyone who can already run code as you is not stopped by either mechanism, so if that is your threat model, the prompt is not where the fix lives.

To let a script make the edit, with a backup and an undo, use Touch ID for sudo. If you also build with a JDK from SDKMAN, this fix is the same kind of one-time macOS setup, for Xcode and java_home.

All writingNextTouch ID for sudo stopped working: the causes and the fixes