feat: browser-based OAuth login flow (story 406)
Add three HTTP endpoints for OAuth login without terminal access: - GET /oauth/authorize — generates PKCE params, redirects to claude.com/cai/oauth/authorize with code=true and full scopes - GET /callback — exchanges auth code for tokens via JSON POST to platform.claude.com/v1/oauth/token, writes ~/.claude/.credentials.json - GET /oauth/status — returns current credential state as JSON Uses SHA-256 (sha2 crate) for PKCE code challenge. The authorize URL targets claude.com/cai/ (not platform.claude.com) which is required for Max/Pro subscriptions to grant user:inference scope. Users visit http://localhost:3001/oauth/authorize in their browser to authenticate. Matrix/WhatsApp can send this link when auth fails. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ pub mod health;
|
||||
pub mod io;
|
||||
pub mod mcp;
|
||||
pub mod model;
|
||||
pub mod oauth;
|
||||
pub mod settings;
|
||||
pub mod workflow;
|
||||
|
||||
@@ -65,6 +66,8 @@ pub fn build_routes(
|
||||
|
||||
let (api_service, docs_service) = build_openapi_service(ctx_arc.clone());
|
||||
|
||||
let oauth_state = Arc::new(oauth::OAuthState::new(resolve_port()));
|
||||
|
||||
let mut route = Route::new()
|
||||
.nest("/api", api_service)
|
||||
.nest("/docs", docs_service.swagger_ui())
|
||||
@@ -78,6 +81,18 @@ pub fn build_routes(
|
||||
post(mcp::mcp_post_handler).get(mcp::mcp_get_handler),
|
||||
)
|
||||
.at("/health", get(health::health))
|
||||
.at(
|
||||
"/oauth/authorize",
|
||||
get(oauth::oauth_authorize).data(oauth_state.clone()),
|
||||
)
|
||||
.at(
|
||||
"/callback",
|
||||
get(oauth::oauth_callback).data(oauth_state.clone()),
|
||||
)
|
||||
.at(
|
||||
"/oauth/status",
|
||||
get(oauth::oauth_status),
|
||||
)
|
||||
.at("/assets/*path", get(assets::embedded_asset))
|
||||
.at("/", get(assets::embedded_index))
|
||||
.at("/*path", get(assets::embedded_file));
|
||||
|
||||
Reference in New Issue
Block a user