fix(cli): platform-specific keychain store hints after login (#235)#265
fix(cli): platform-specific keychain store hints after login (#235)#265adriannoes wants to merge 1 commit into
Conversation
Branch error messages on macOS, Linux, and Windows when OAuth succeeds but store_session fails. Document the macOS errSecParam (-25244) ACL workaround in docs/cli/auth.md.
| The login worked but `keyring` couldn't write the entry. | ||
|
|
||
| When `PIPEFY_KEYCHAIN_BACKEND=file` is active the backend reports as `PlaintextKeyring` and the hint switches to a config-directory writability check (the file backend writes to `keyring.cfg` under the resolved config directory). | ||
| **macOS (Keychain / `Keyring` backend).** OAuth can succeed while persistence fails with `Can't store password on keychain: (-25244, 'Unknown Error')`. That code is `errSecParam` from Security.framework — common when the calling process cannot surface the new-item ACL dialog (IDE slash commands, agent hosts, and other non-TTY subprocesses that do not share an interactive Aqua session). The fix is independent of how `pipefy` was installed (`uvx`, `uv tool install`, or a wheel). |
There was a problem hiding this comment.
suggestion (non-blocking): "Independent of how pipefy was installed" reads as "grant once, forever" to the reader who lands here after a failure. That holds for uv tool install and wheel installs (stable Python interpreter path, so the macOS keychain ACL persists), but uvx uses cached ephemeral environments whose binary path can change after uvx --refresh or a uv version bump. When the path changes, the existing Always Allow grant no longer applies and the next write surfaces the same errSecParam. Worth qualifying so uvx users don't conclude the workaround silently broke.
Suggested rewrite of the trailing sentence:
The remediation procedure is the same regardless of install method. macOS binds the keychain ACL to the calling Python binary's path, so
uv tool installand a wheel install give you a stable grant; withuvx, the cached environment's binary path can change (for example afteruvx --refreshor a uv version bump), and macOS will prompt again from Terminal.app.
| ("Darwin", "errSecParam"), | ||
| ("Darwin", "Terminal.app"), | ||
| ("Darwin", "Always Allow"), |
There was a problem hiding this comment.
nitpick (non-blocking): the three Darwin rows run the same call against the same patched platform.system three times to assert three substrings of one hint. The same shape leaves no symmetric check that the Linux or Windows hints don't accidentally pick up another platform's wording.
Consider collapsing to one row per platform with required/forbidden fragment lists:
@pytest.mark.parametrize(
("system", "required", "forbidden"),
[
("Darwin", ["errSecParam", "Terminal.app", "Always Allow"], ["Secret Service"]),
("Linux", ["Secret Service"], ["Terminal.app", "Credential Manager"]),
("Windows", ["Credential Manager"], ["Secret Service", "Terminal.app"]),
("FreeBSD", ["pipefy auth status"], []),
],
)The CLI test in test_auth_login.py already covers cross-platform leakage at the integration layer, so this is a readability cleanup, not a coverage gap.
| @@ -0,0 +1,60 @@ | |||
| """Platform-specific hints when OS keychain session storage fails after OAuth.""" | |||
There was a problem hiding this comment.
nitpick (non-blocking): every other module in commands/ without a leading underscore registers a Typer app and is a public CLI surface; the one existing helper (_common.py) carries the underscore. This module is also a helper (one function, called only from auth.py), so the name is slightly inconsistent with the directory convention.
Consider renaming to _auth_keychain_hints.py and updating the two import sites (commands/auth.py and tests/test_auth_keychain_hints.py). The test filename can stay as-is; pytest discovery doesn't require name parity with the SUT.
Defer freely; this is a one-time naming nit, not a coverage or correctness concern.
gbrlcustodio
left a comment
There was a problem hiding this comment.
Inline notes are all non-blocking; nothing here blocks merge.
Summary
auth_keychain_hints.pywith platform-specific remediation text whenpipefy auth loginsucceeds at OAuth butstore_sessionfails.errSecParam (-25244)and the Terminal.app + Always Allow ACL workaround instead of the headless Linux Secret Service hint.PIPEFY_KEYCHAIN_BACKEND=filehint and add a Windows Credential Manager variant.docs/cli/auth.md.Closes #235.