Enable Claude OAuth login without requiring API keys

This adds an end-to-end OAuth PKCE login/logout path to the Rust CLI,
persists OAuth credentials under the Claude config home, and teaches the
API client to use persisted bearer credentials with refresh support when
env-based API credentials are absent.

Constraint: Reuse existing runtime OAuth primitives and keep browser/callback orchestration in the CLI
Constraint: Preserve auth precedence as API key, then auth-token env, then persisted OAuth credentials
Rejected: Put browser launch and token exchange entirely in runtime | caused boundary creep across shared crates
Rejected: Duplicate credential parsing in CLI and api | increased drift and refresh inconsistency
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Keep logout non-destructive to unrelated credentials.json fields and do not silently fall back to stale expired tokens
Tested: cargo fmt; cargo clippy --workspace --all-targets -- -D warnings; cargo test
Not-tested: Manual live Anthropic OAuth browser flow against real authorize/token endpoints
This commit is contained in:
Yeachan-Heo
2026-03-31 23:38:05 +00:00
parent d5d99af2d0
commit 32e89df631
10 changed files with 753 additions and 27 deletions

View File

@@ -31,6 +31,10 @@ pub enum Command {
DumpManifests,
/// Print the current bootstrap phase skeleton
BootstrapPlan,
/// Start the OAuth login flow
Login,
/// Clear saved OAuth credentials
Logout,
/// Run a non-interactive prompt and exit
Prompt { prompt: Vec<String> },
}
@@ -86,4 +90,13 @@ mod tests {
})
);
}
#[test]
fn parses_login_and_logout_commands() {
let login = Cli::parse_from(["rusty-claude-cli", "login"]);
assert_eq!(login.command, Some(Command::Login));
let logout = Cli::parse_from(["rusty-claude-cli", "logout"]);
assert_eq!(logout.command, Some(Command::Logout));
}
}