Security

Your tokens. Your keys. We can't read them.

TapAuth is built on a zero-knowledge architecture. We broker OAuth tokens for AI agents — but we never have the ability to decrypt what we store.

🛡️

Your LLM Never Sees Your Credentials

The problem: Most agent auth solutions pass credentials through the LLM's context window. If the conversation is logged, cached, or leaked — so are your keys.

How TapAuth solves it: The TapAuth CLI runs as a subprocess. It handles all credential operations — grant creation, secret storage, token retrieval — in a shell process the LLM never reads. The LLM calls the CLI, the CLI returns a bearer token. The grant secret (which is the decryption key) stays in ~/.tapauth/grants.json on your machine.

Even if your entire LLM conversation is logged or exfiltrated, no credentials are exposed. The agent gets a short-lived bearer token — not the keys to the kingdom.

🔐

Zero-Knowledge Token Storage

How it works: Every grant's tokens are encrypted with a unique key derived from the grant_secret using HKDF-SHA256. TapAuth never stores the grant_secret or the derived encryption key.

What we store: A one-way verification hash (cryptographically independent from the encryption key) and the encrypted token blob. That's it.

What this means: Even with full database access, stored tokens are unreadable ciphertext. There is no master key, no admin override, no backdoor. Same model as 1Password's Secret Key.

Current state: Tokens are currently encrypted at rest with AES-256-GCM. Per-grant zero-knowledge encryption is shipping soon — making TapAuth unable to decrypt stored tokens even with full server access.

🔑

You Hold the Only Key

The grant_secret: When you create a grant, you receive a grant_secret (prefixed gs_). This isn't just an API key — it's the root of your encryption key. TapAuth returns it once and never stores it.

What happens if you lose it: Tokens are permanently unrecoverable. By design. The agent must create a new grant and the user must re-approve. This is a feature, not a bug.

Revocation: Delete the grant, tokens are gone. No lingering access, no orphaned credentials.

🎯

Blast Radius of One

Per-grant isolation: Each grant has its own encryption key, its own scoped OAuth permissions, its own lifecycle. Compromising one grant_secret exposes exactly one grant's tokens — nothing else.

Contrast: Traditional OAuth brokers encrypt all tokens with a single server key. One breach = every user's tokens. TapAuth's per-grant encryption means one breach = one grant.

Scoped by design: Grants request only the specific OAuth scopes needed. A grant for calendar.readonly can't read your email.

Threat Model

Attacker has...What they get
Database access onlyEncrypted blobs. No keys to decrypt them.
Server environment (ENV) onlyNo tokens to decrypt.
Database + ENV accessNothing (post zero-knowledge migration). Legacy grants only during transition.
One grant_secretThat one grant's tokens. Nothing else.
Full server compromiseOnly unclaimed tokens in ≤10 minute staging window.

The Technical Details

Encryption:AES-256-GCM (authenticated encryption, tamper-evident)
Key derivation:HKDF-SHA256 with domain-separated info parameters
Secret generation:CSPRNG (crypto.randomBytes), 32 bytes of entropy
Verification:Timing-safe comparison (crypto.timingSafeEqual) — no timing oracles
Token lifecycle:Access tokens are short-lived per provider policy. Refresh handled automatically. Refresh tokens encrypted per-grant.
No account required:OAuth approval IS registration. No passwords to breach, no user database to leak.
Human-in-the-loop:Every grant requires explicit user approval via browser. No silent background auth.

Have security questions?