Skip to content

fix(auth): add dpapi keychain backend for oversized Windows sessions#409

Draft
richard-pipefy wants to merge 1 commit into
pipefy:devfrom
richard-pipefy:fix/windows-keychain-dpapi-backend
Draft

fix(auth): add dpapi keychain backend for oversized Windows sessions#409
richard-pipefy wants to merge 1 commit into
pipefy:devfrom
richard-pipefy:fix/windows-keychain-dpapi-backend

Conversation

@richard-pipefy

Copy link
Copy Markdown

Summary

On Windows, pipefy auth login fails at the session-store step with WinError 1783 ("The stub received bad data") from CredWrite. The stored session (access + refresh + id tokens plus metadata, UTF-16 encoded by keyring) exceeds Windows Credential Manager's CRED_MAX_CREDENTIAL_BLOB_SIZE (2560 bytes; the exact size varies with token claims), so the default auto backend cannot store it.

This adds an opt-in dpapi keychain backend — a DPAPI-encrypted file keyring (keyrings.alt.Windows.EncryptedKeyring) with no blob cap and encrypted at rest. Selected via PIPEFY_KEYCHAIN_BACKEND=dpapi (or keychain_backend = "dpapi" in config.toml), read by both the CLI and the MCP server, so one per-user entry covers both processes. Selecting dpapi off Windows raises a clear error. The default auto and the plaintext file backend are unchanged.

No new runtime dependency: keyrings.alt is already present, and the DPAPI path uses raw ctypes against CRYPT32.DLL (no pywin32).

Why a new backend instead of PIPEFY_KEYCHAIN_BACKEND=file?

file (PlaintextKeyring) also sidesteps the blob cap and would make the login succeed — the crash is specific to Credential Manager, which only auto routes to. The difference is at-rest encryption, and it's the whole reason this backend exists:

  • file writes the session — including the long-lived refresh token — to disk in plaintext. Anything that can read the user's config dir (local malware, a synced/backed-up folder, another admin) gets a working credential.
  • That's a downgrade from what the default auto backend already gives macOS (Keychain) and Linux (Secret Service) users: an encrypted-at-rest store. file is intended as the "no keychain available" fallback for headless/CI runners, not the recommended path for real user machines.
  • dpapi restores encrypted-at-rest parity on Windows — same one-env-var operational cost as file, but the token is DPAPI-encrypted and user-scoped.

Net: use file only where plaintext-at-rest is explicitly acceptable; dpapi is the right default for real Windows users who hit the blob cap.

Testing performed

  • uv run ruff check packages/auth packages/cli → All checks passed
  • uv run ruff format --check packages/auth packages/cli → already formatted
  • uv run pytest packages/auth/tests -q181 passed
  • uv run pytest packages/cli/tests -q -k auth94 passed, 3 skipped
  • New unit tests cover the off-Windows rejection and the on-Windows EncryptedKeyring install (packages/auth/tests/test_storage_backend.py).
  • Manual E2E: a real pipefy auth login round-trip on Windows with PIPEFY_KEYCHAIN_BACKEND=dpapi completes and the session reloads (the auto backend fails the same login with WinError 1783).

Docs updated

  • docs/config.mdkeychain_backend values
  • docs/cli/auth.md — env-var row, WinError 1783 troubleshooting entry
  • CLI login-failure hint now points Windows users at PIPEFY_KEYCHAIN_BACKEND=dpapi
  • CHANGELOG.md — Fixed entry under ## [Unreleased]

Notes

  • Opt-in only; no behavior change for existing macOS/Linux users.

On Windows, `pipefy auth login` failed at the session-store step with
WinError 1783 ("The stub received bad data") from CredWrite. The stored
session (access + refresh + id tokens plus metadata, UTF-16 encoded by
keyring) exceeds Windows Credential Manager's CRED_MAX_CREDENTIAL_BLOB_SIZE
(2560 bytes; the exact size varies with token claims), so the default
`auto` backend cannot store it.

Add `PIPEFY_KEYCHAIN_BACKEND=dpapi` (keychain_backend = "dpapi" in TOML):
a DPAPI-encrypted file keyring (keyrings.alt.Windows.EncryptedKeyring),
encrypted at rest and with no blob cap. Read from config.toml by both the
CLI and MCP server, so one per-user entry covers both processes. Opt-in;
selecting dpapi off-Windows raises. The plaintext `file` backend is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant