Knock, knock. Who’s there? Touch ID. Again.
Picture this: you’re working in your terminal, pulling secrets from 1Password with op read. You need the Linear API key. Touch ID. The OpenRouter one. Touch ID. The Gitea one. Touch ID.
In half an hour it asked for my finger fourteen times.
You know what happens when a security tool interrupts you fourteen times in thirty minutes? By the fifth time you’re not reading what it’s asking for. You put your finger down like a reflex. “Yeah, whatever, let me work.”
And that’s exactly where security goes to hell.
Auth fatigue: the problem nobody wants to see
This has a name in security: authorization fatigue. And it’s not a new concept. It’s the same principle used in MFA fatigue attacks: bombard the user with authorization requests until they accept one out of pure exhaustion.
In 2022, a 17-year-old kid broke into Uber’s internal systems exactly like this. He sent authentication push notifications to the employee over and over, in the middle of the night, until the guy accepted one just to get some sleep.
Obviously, 1Password asking for Touch ID isn’t an attack. But the psychological effect is identical: it trains you to approve without thinking.
It’s like those cookie banners that have been popping up on every website for years. At first you read them. Now you hit “Accept all” without looking. Congratulations: a mechanism designed to protect your privacy has taught you to give away your privacy faster.
Why 1Password kept asking for my finger
My setup: I use op read to pull secrets from 1Password through the terminal. Works great. The problem is I use Claude Code (an AI assistant in the terminal), and every command it runs is a new process.
1Password has a biometric session timeout of 10 minutes of inactivity, and it refreshes with each use. In theory, it shouldn’t ask for the finger so often. But Claude Code doesn’t reuse processes: every time it needs a secret, it launches a new shell, and 1Password interprets it as a new session.
Result: Touch ID every time Claude needs a secret. Which is constantly.
The solution: a 40-line cache
The idea is simple: a wrapper that sits in front of op in the PATH. When you do op read, it checks if it already has the result cached and fresh. If yes, it returns it without touching 1Password. If not, it calls the real op, caches the result, and done.
For any other subcommand (op signin, op item list, etc.), it passes through directly to the real op without interfering.
| |
You save it as ~/.local/bin/op, give it execution permissions, and since ~/.local/bin comes before /opt/homebrew/bin in the PATH, your wrapper intercepts the calls.
The security decisions
Let’s be honest about what we’re doing: storing secrets in plain text on disk. That sounds terrible. But let’s put it in context.
What it caches:
- Only results from
op read(individual secret reads) - Everything else passes directly to the real
op
Where it saves it:
~/.cache/op-cache/with700permissions (your user only)- Each cache file with
600permissions (read/write for you only)
How long it lasts:
- 1 hour by default, configurable with
OP_CACHE_TTL
The filenames:
- Are SHA-256 hashes of the arguments, don’t reveal which secret they contain
Is it more dangerous than a .env.local? No. It’s exactly the same thing. Your .env.local files are also plain text secrets on disk with restrictive permissions. And you have those in every project.
Is it more dangerous than what 1Password already does? The 1Password app keeps your vault decrypted in memory while it’s unlocked. Our cache is more limited (only secrets you’ve read, not the whole vault) but less sophisticated (disk vs memory).
What worries us (and we don’t know how to solve)
Here comes the honest part. We’re not security experts. We’ve made decisions that seem reasonable to us, but we might be missing something. Some concerns:
1. Should the cache clear when locking the screen? Right now, if you lock your Mac and someone accesses the disk (theft, evil maid), the cached secrets are there. Though if someone has access to your disk, you probably have bigger problems (FileVault should protect against this).
2. Are there race conditions?
If two processes do op read of the same secret at once, both could try to write the cache simultaneously. In practice it shouldn’t cause serious problems (worst case is a partial read), but it’s not pretty.
3. Is the hash sufficient?
We use SHA-256 of the arguments as the filename. If someone has access to ~/.cache/op-cache/, they can’t know which secret is in each file, but they can read the content of all of them. The 600 permissions should prevent this, but if there’s a compromised process running as your user…
What could be improved
Some ideas we haven’t implemented (for now):
- Automatic cleanup of expired files (a
cronorlaunchdthat purges periodically) - Cache encryption with a key derived from the session
- Notification when a secret is served from cache ("(cached)" to stderr)
- Manual invalidation with
op cache clearor similar
This is where you come in
Look, I’m writing this with the honesty of someone who knows they’re not a security expert. We’ve made decisions that seem sensible to us. The threat model is clear: protect ourselves from authorization fatigue without opening obvious holes.
But “seems sensible to us” and “is secure” are two very different things.
If you know more about security than we do (which isn’t hard) and you see an obvious flaw, an edge case we haven’t considered, or simply a better way to do this: tell me. Seriously. The comments are open and so is my email.
I’d rather have someone tell me “what you’ve done is a dangerous hack” than find out when it’s too late.
The elephant in the room
Should 1Password solve this out of the box? Yes, probably. A configurable timeout per application, or a “work session” mode that kept authorization active for a defined period, would eliminate the need for this wrapper.
But while they don’t, the alternative is worse: keep putting your finger down every 30 seconds until your brain disconnects and you start approving without looking.
Because that’s what’s paradoxical about excessive security: if the tool bothers you too much, you end up being less secure than if you didn’t use it. At least without it you’re aware you’re unprotected. With authorization fatigue, you think you’re protected while approving anything with your eyes closed.
The best lock in the world is useless if the owner leaves the door open because they’re tired of looking for the key.
Related: If you’re interested in why we centralize all secrets in 1Password, read 39 million secrets leaked on GitHub. And if you want to see what happens when you give too many capabilities to an AI (spoiler: it sends 44 fake emails), When your AI becomes your worst enemy is the horror story.