Skip to main content

entelix_auth_claude_code/
lib.rs

1//! # entelix-auth-claude-code
2//!
3//! Claude Code OAuth credential provider for entelix.
4//!
5//! Reuses the Claude.ai access token the `claude` CLI manages
6//! (`~/.claude/.credentials.json` by default), refreshing through
7//! the standard OAuth2 `refresh_token` grant against
8//! `https://console.anthropic.com/v1/oauth/token` when the access
9//! token approaches expiry.
10//!
11//! ## Why
12//!
13//! Operators with a Claude.ai pro / team subscription get to drive
14//! entelix without minting a separate Anthropic API key — the same
15//! credential their `claude` CLI uses flows straight into the
16//! `entelix_core::auth::CredentialProvider` chain. Refresh-token
17//! rotation is handled automatically and storage stays compatible
18//! with the upstream CLI, so both tools share state.
19//!
20//! ## Layout
21//!
22//! ```text
23//! ClaudeCodeOAuthProvider          — impl entelix_core::auth::CredentialProvider
24//!   └─ CredentialStore trait       — operator-supplied backend
25//!         └─ FileCredentialStore   — default; reads / writes the on-disk JSON
26//!   └─ ClaudeCodeOAuthConfig       — token URL / client id / refresh timeout
27//!   └─ refresh_access_token        — RFC 6749 §6 grant
28//! ```
29//!
30//! [`CredentialStore`] is a trait so vault / Keychain / Secret
31//! Service backends plug in by implementing it; the default ships
32//! only the file backend so the dependency footprint stays minimal.
33//!
34//! ## Beta capability gate
35//!
36//! Claude Code's OAuth tokens require the `claude-code-20250219`
37//! anthropic-beta header. The provider deliberately *does not*
38//! inject the header — credentials and codec ext stay independent
39//! (single responsibility). Operators wire it through
40//! [`entelix_core::ir::AnthropicExt::with_betas`] using
41//! [`CLAUDE_CODE_BETA`]:
42//!
43//! ```ignore
44//! use entelix_auth_claude_code::{
45//!     CLAUDE_CODE_BETA, ClaudeCodeOAuthProvider, FileCredentialStore,
46//! };
47//! use entelix_core::ir::{AnthropicExt, ProviderExtensions};
48//!
49//! let store = FileCredentialStore::with_path(
50//!     FileCredentialStore::default_claude_path()?,
51//! );
52//! let provider = ClaudeCodeOAuthProvider::new(store);
53//! // Apply the matching beta capability on every outgoing request:
54//! let extensions = ProviderExtensions::default()
55//!     .with_anthropic(AnthropicExt::default().with_betas([CLAUDE_CODE_BETA]));
56//! ```
57//!
58//! ## Store hygiene
59//!
60//! [`FileCredentialStore`] reads and writes the credential file on a
61//! `tokio::task::spawn_blocking` worker so the async runtime never
62//! stalls on disk IO. Operators that need an in-memory backend
63//! (env-var-driven, vault) implement [`CredentialStore`] directly
64//! without touching the file path.
65
66#![cfg_attr(docsrs, feature(doc_cfg))]
67#![doc(html_root_url = "https://docs.rs/entelix-auth-claude-code/0.5.3")]
68#![deny(missing_docs)]
69// Doc-prose lints fire on legitimate proper nouns / acronyms (Claude
70// Code, OAuth2, RFC 6749, …); the redundant_pub_crate lint disagrees
71// with the workspace `unreachable_pub` rule for items inside private
72// modules — same exemption pattern as `entelix-tools`.
73#![allow(clippy::doc_markdown, clippy::redundant_pub_crate)]
74
75mod config;
76mod credential;
77mod error;
78mod provider;
79mod refresh;
80mod store;
81
82pub use config::{
83    CLAUDE_CODE_BETA, ClaudeCodeOAuthConfig, DEFAULT_REFRESH_TIMEOUT, DEFAULT_TOKEN_URL,
84};
85pub use credential::{CredentialFile, OAuthCredential};
86pub use error::{ClaudeCodeAuthError, ClaudeCodeAuthResult};
87pub use provider::ClaudeCodeOAuthProvider;
88pub use store::{CredentialStore, FileCredentialStore};