Add keyring credential storage and keyring:// URI support#55
Merged
Michel Edkrantz (MichelEdkrantz) merged 20 commits intomasterfrom Feb 20, 2026
Merged
Add keyring credential storage and keyring:// URI support#55Michel Edkrantz (MichelEdkrantz) merged 20 commits intomasterfrom
Michel Edkrantz (MichelEdkrantz) merged 20 commits intomasterfrom
Conversation
Store OAuth tokens in the system keyring so subsequent CLI invocations (kognic-auth get-access-token, kog) can skip the token fetch when a valid cached token exists. Tokens are scoped per auth_server:client_id. - New module: cli/token_cache.py with load/save/clear functions - Add --no-cache flag to both CLI commands to bypass caching - keyring is an optional dependency (graceful degradation if absent) - Library API (RequestsAuthSession, BaseApiClient, etc.) is unaffected https://claude.ai/code/session_015vu8mYThdaZcoJXS4xWvDQ
Separate token management from HTTP session configuration: - RequestsAuthSession gains ensure_token() and invalidate_token() as first-class methods - _KognicBearerAuth (requests.AuthBase) injects Bearer tokens per request and handles 401 by invalidating and retrying once - create_session builds a plain requests.Session per call, eliminating monkey-patch stacking when multiple clients share credentials - Module-level WeakValueDictionary pool keyed on (client_id, auth_host, token_endpoint) ensures BaseApiClient instances with the same credentials share one token provider by default - BaseApiClient accepts explicit token_provider= for opt-in sharing or test injection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TokenCache and its implementations (KeyringTokenCache, FileTokenCache, make_cache) move from cli/ to internal/ so they can be used by both CLI commands and library clients. BaseApiClient gains a token_cache parameter that enables cross-process token persistence. The shared provider pool key now includes the cache type so cached and uncached clients for the same credentials get separate providers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
_base.py – TokenCache ABC + helpers (make_key, is_valid, constants) _keyring.py – KeyringTokenCache _file.py – FileTokenCache __init__.py – re-exports + make_cache factory All existing import paths are unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers: - Same credentials share one provider instance - Different credentials, auth hosts, or cache types get separate providers - Explicit token_provider bypasses the pool entirely - Pool entries are held weakly and GC'd when no client references them Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both CLI commands shared duplicated credential+cache wiring. The new make_token_provider() in base_client.py centralises this: it resolves credentials and wires up initial_token/on_token_updated from the cache in one place. get_access_token now calls make_token_provider() + ensure_token() instead of constructing RequestsAuthSession directly. api_request's _create_authenticated_session now calls make_token_provider() + create_session(token_provider=). test_cli.py updated to match the current interface (--token-cache choices, make_token_provider/make_cache patch targets). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces `kognic-auth credentials load/clear` subcommands to store full credentials in the system keyring. Adds `keyring://profile` URI support in resolve_credentials and env_config for per-environment keyring lookups. The keyring is also checked as a fallback in get_credentials_from_env. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Base automatically changed from
claude/keyring-token-storage-ACjSc
to
master
February 20, 2026 12:16
# Conflicts: # src/kognic/auth/__init__.py # src/kognic/auth/cli/__init__.py # src/kognic/auth/cli/api_request.py # src/kognic/auth/cli/get_access_token.py # src/kognic/auth/credentials_parser.py # src/kognic/auth/requests/base_client.py # tests/test_cli.py # tests/test_credentials_parser.py
- README: fix synopsis showing --profile instead of --env - credentials_store.py: move parse_credentials import inside load_credentials to break circular dependency with credentials_parser.py - credentials_parser.py: add missing credentials_store import - Rename credential_store.py -> credentials_store.py (and test file) - Update all references and mock patch paths accordingly Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kognic-auth credentials load <file> [--env ENV]andcredentials clear [--env ENV]subcommands to store/remove full credentials in the system keyring (macOS Keychain, GNOME Keyring, Windows Credential Manager, etc.)keyring://profileURI support inresolve_credentialsandenv_config, enabling per-environment keyring lookups via"credentials": "keyring://production"inenvironments.jsonget_credentials_from_env(checked after env vars)client_id/client_secret--profileinstead of--envcredentials_parser.pyandcredentials_store.pyby movingparse_credentialsimport insideload_credentials()function bodycredential_store.py→credentials_store.py(and test file) for consistencyTest plan
uv run python -m pytest— all 235 tests passkognic-auth credentials load ~/Downloads/credentials.jsonstores credentials in system keyringkognic-auth credentials load ~/Downloads/prod.json --env production+"credentials": "keyring://production"inenvironments.jsonresolves correctly withkog get https://app.kognic.com/...kognic-auth credentials clearremoves credentials from keyringBaseApiClient(auth="keyring://production")resolves credentials from keyring🤖 Generated with Claude Code