agent-auth
README.md
# agent-auth
Zero-knowledge credential injection for AI agents. Your agent authenticates to websites and APIs without ever seeing a password, TOTP code, or API key.
```
Agent says: fill #email with {{email}}, fill #password with {{password}}, click Sign In
agent-auth: resolves {{email}} and {{password}} from encrypted vault, injects into browser
Agent gets: "Login completed." (never sees the real values)
```
## Why
AI agents are getting good at browsing the web and calling APIs. But authentication is a wall — you either hand the agent your password (dangerous) or do it yourself every time (defeats the purpose).
agent-auth sits between the agent and the browser. The agent describes *what to do* with placeholder tokens. agent-auth resolves the real credentials from a local encrypted vault and injects them directly into the browser via CDP. The agent never touches the secret material.
Works with any MCP-compatible agent: Claude Code, Claude Desktop, OpenCode, Cursor, or your own.
## How It Works
```
AI Agent agent-auth Browser
| | |
| "Log into AWS" | |
| steps: [ | |
| fill #email {{email}} | |
| fill #pass {{password}} | 1. Decrypt from local vault |
| fill #totp {{totp}} | 2. Generate TOTP from seed |
| click Submit | 3. Inject via CDP --------->| Form filled
| ] | 4. Zero memory |
| | |
| "Login completed" <--------- | |
| | |
| (never saw any secrets) | (secrets wiped from RAM) |
```
## Quick Start
```bash
# Clone and install
git clone https://github.com/ex-nihilo-labs/agent-auth.git
cd agent-auth && bun install
# Create your vault (you'll set a passphrase)
bun run src/index.ts init
# Add a credential
bun run src/index.ts add github \
--username "you@example.com" \
--password "your-password" \
--domains "github.com"
# Add one with TOTP
bun run src/index.ts add aws-root \
--username "admin@company.com" \
--password "hunter2" \
--totp "JBSWY3DPEHPK3PXP" \
--domains "signin.aws.amazon.com,console.aws.amazon.com"
# See what's stored (names only — no secrets shown)
bun run src/index.ts list
# → github (domains: github.com)
# → aws-root (domains: signin.aws.amazon.com, console.aws.amazon.com)
```
## Connect to Your Agent
agent-auth is an MCP server. Add it to your agent's config:
### Claude Code / Claude Desktop
Add to `.mcp.json` in your project root (or `~/.claude/settings.json` for global):
```json
{
"mcpServers": {
"agent-auth": {
"type": "stdio",
"command": "bun",
"args": ["run", "/path/to/agent-auth/src/index.ts", "serve"],
"env": {
"AGENT_AUTH_CDP_URL": "http://localhost:9222"
}
}
}
}
```
The passphrase is read from your OS keychain automatically (stored during `init`). For development, you can set `AGENT_AUTH_PASSPHRASE` in the env block instead.
### Other MCP Agents
Any agent that speaks [MCP](https://modelcontextprotocol.io/) over stdio can use agent-auth. Start the server:
```bash
bun run src/index.ts serve
```
It exposes three tools over stdin/stdout JSONRPC.
## MCP Tools
| Tool | What the agent sends | What the agent gets back |
|------|---------------------|------------------------|
| **`secure_login`** | Service name + browser steps with `{{placeholders}}` | "Login completed" or error |
| **`auth_api`** | Service name + HTTP request details | API response body (credentials redacted) |
| **`list_credentials`** | *(nothing)* | Service names and allowed domains only |
### secure_login
Browser-based authentication. The agent describes the login flow as steps:
```json
{
"service": "github",
"url": "https://github.com/login",
"steps": [
{ "action": "fill", "selector": "#login_field", "value": "{{email}}" },
{ "action": "fill", "selector": "#password", "value": "{{password}}" },
{ "action": "click", "selector": "input[type='submit']" },
{ "action": "wait", "selector": ".logged-in", "timeout": 5000 }
]
}
```
Step actions: `fill`, `type` (character-by-character for SPAs), `click`, `wait`, `select`.
### auth_api
Authenticated HTTP requests. Six injection methods:
```json
{
"service": "openai",
"url": "https://api.openai.com/v1/models",
"method": "GET",
"injection": "bearer"
}
```
Injection methods: `bearer`, `header`, `query`, `basic`, `json_body`, `form`.
### list_credentials
Returns service names and allowed domains. Never returns passwords, TOTP seeds, or API keys.
## Security Model
**The core guarantee: credentials never appear in MCP responses.** They flow from vault to browser/HTTP and are zeroed from memory immediately after.
| Layer | Implementation |
|-------|---------------|
| **Encryption at rest** | AES-256-GCM, 12-byte random nonce per field |
| **Key derivation** | Argon2id (3 iterations, 64MB memory, 4 parallelism) |
| **Master key storage** | OS keychain (macOS Keychain, Linux secret-tool) with encrypted file fallback |
| **Memory hygiene** | All credentials as `Buffer`/`Uint8Array`, zeroed with `buf.fill(0)` after use. Never converted to JS strings (immutable, can't be wiped). |
| **Domain allowlist** | Deny-by-default. Each credential lists which domains it can be injected into. |
| **Redirect protection** | Domain re-verified after every navigation step. Aborts if redirect leaves the allowlist. |
| **Human approval** | First use of each credential+domain pair sends a push notification (Pushover) with a 4-digit code. 50-second window. |
| **Rate limiting** | 3 requests/minute, 20/hour. Persisted in SQLite across restarts. |
| **Audit trail** | Append-only JSONL log. All credential values masked. |
| **No cloud** | Everything local. Vault never synced, uploaded, or phoned home. |
## CLI Reference
All CLI commands are human-only — never exposed via MCP.
```bash
agent-auth init # Create vault, set passphrase
agent-auth add <service> # Add credential (interactive or with flags)
agent-auth list # List services (names only)
agent-auth remove <service> # Delete a credential
agent-auth domains <service> # View/edit allowed domains
agent-auth approve <code> # Approve a pending auth request
agent-auth unlock # Unlock vault for current session
agent-auth lock # Lock vault, clear key from memory
agent-auth serve # Start MCP server (stdio)
```
**Flags for `add`:** `--username`, `--password`, `--totp` (base32 or otpauth:// URI), `--domains` (comma-separated), `--notes`.
## Environment Variables
| Variable | Purpose | Default |
|----------|---------|---------|
| `AGENT_AUTH_CDP_URL` | Chrome DevTools Protocol endpoint for browser injection | *(none — browser tools disabled)* |
| `AGENT_AUTH_PASSPHRASE` | Vault passphrase (dev/CI only — use keychain in production) | *(prompt or keychain)* |
| `AGENT_AUTH_NO_KEYCHAIN` | Skip OS keychain, use file-only key storage | `false` |
| `AGENT_AUTH_PUSHOVER_TOKEN` | Pushover app token for approval notifications | *(approval disabled)* |
| `AGENT_AUTH_PUSHOVER_USER` | Pushover user key | *(approval disabled)* |
## Architecture
```
agent-auth/
├── src/
│ ├── index.ts # CLI dispatcher + MCP serve
│ ├── mcp/ # MCP server (3 tools over stdio JSONRPC)
│ ├── browser/ # CDP injection via Playwright (fill/type/click/wait/select)
│ ├── placeholder/ # {{email}}, {{username}}, {{password}}, {{totp}} resolution
│ ├── vault/ # AES-256-GCM encrypted SQLite + Argon2id KDF
│ ├── totp/ # TOTP generation via otpauth
│ ├── proxy/ # HTTP credential injection (6 methods)
│ ├── approval/ # Human approval gate + Pushover
│ ├── security/ # Rate limiter, input validator, domain allowlist
│ ├── audit/ # Append-only JSONL audit log
│ └── cli/ # Human-only credential management
└── tests/ # 65 tests across 6 suites
```
**Vault location:** `~/.agent-auth/vault.db` (SQLite, mode 0600)
**Dependencies (intentionally minimal):**
- `@modelcontextprotocol/sdk` — MCP protocol
- `playwright-core` — CDP browser automation (no bundled browser)
- `bun:sqlite` — Built-in SQLite (zero deps)
- `otpauth` — TOTP generation (5KB, pure JS)
- `@noble/hashes` — Argon2id KDF (audited, pure JS)
- `zod` — Input validation
## Testing
```bash
bun test # 65 tests, 6 suites
bun run typecheck # TypeScript strict mode
```
Tests use `AGENT_AUTH_NO_KEYCHAIN=1` to avoid macOS Keychain permission dialogs in CI.
## Acknowledgments
agent-auth was inspired by these projects:
- **[AgentSecrets](https://github.com/The-17/agentsecrets)** (MIT) — Crypto envelope design (AES-256-GCM + Argon2id), domain allowlists, HTTP credential proxy pattern.
- **[Virtual FIDO](https://github.com/bulwarkid/virtual-fido)** (MIT) — Passkey/WebAuthn emulation architecture. Planned for future passkey support.
## Roadmap
- [ ] Passkey/WebAuthn support via [Virtual FIDO](https://github.com/bulwarkid/virtual-fido) signing
- [ ] `npx agent-auth` / global install
- [ ] Credential import from 1Password, Bitwarden (CLI export)
- [ ] Browser profile persistence (stay logged in across sessions)
- [ ] Multi-page login flow templates (common services)
## Contributing
PRs welcome. The codebase is small (~1,500 lines) and intentionally simple.
**Ground rules:**
- No cloud features. The vault is local-only. This is not negotiable.
- No new runtime dependencies without justification. We have 5 — that's enough.
- Credentials must never be converted to JS strings. `Buffer`/`Uint8Array` only.
- Tests required for any new functionality.
```bash
# Development
bun install
AGENT_AUTH_NO_KEYCHAIN=1 bun test # Run tests
bun run typecheck # Type check
```
## License
[MIT](LICENSE) — Ex Nihilo Labs, 2026
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues