Remote MCP (OAuth): expired access token forces interactive re-auth instead of silent refresh_token grant
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Shell
- Star
- 11.2k
- Fork
- 1.9k
- Merge trung bình
- 14 giờ 16 phút
- Pull request đã merge (30 ngày)
- 6
Mô tả
Summary
When a remote OAuth-protected MCP server's cached access token is expired at session start, the Copilot CLI treats the server as needing a fresh interactive OAuth login and drops its tools — even though a valid refresh token is cached. It never attempts an RFC 6749 §6 refresh_token grant. In non-interactive/automation sessions the interactive flow can't complete, so the server's tools silently never load.
Environment
- GitHub Copilot CLI
1.0.71, running inside VS Code - macOS (Darwin); credential store = Apple Keychain (
copilot-mcp-oauth) - MCP transport:
rmcpstreamable HTTP (rmcp::transport::worker/::streamable),reqwest 0.13.4
Configuration
- Remote HTTP MCP server in
~/.copilot/mcp-config.json:"notion": { "type": "http", "url": "https://mcp.notion.com/mcp" } - Cached grant in Keychain (
service copilot-mcp-oauth, account<hash>) and~/.copilot/mcp-oauth-config/<hash>.tokens.json— containsaccessToken,expiresAt,refreshToken. - Server authorization-server metadata (RFC 8414) advertises refresh support:
token_endpoint: https://mcp.notion.com/token,grant_types_supported: ["authorization_code", "refresh_token"]
Steps to reproduce
- Authenticate a remote OAuth MCP server (e.g. Notion) so a valid access+refresh grant is cached.
- Let the access token expire while the refresh token stays valid (Notion access tokens last ~8h — e.g. leave it overnight).
- Start a new Copilot CLI session (especially a non-interactive/scheduled one).
- Observe: the server's tools are absent from the session.
Observed behavior
From ~/.copilot/logs/process-<...>.log (redacted):
[ERROR] Starting remote MCP client for notion with url: https://mcp.notion.com/mcp
[ERROR] Connecting MCP client for notion...
-> connects to mcp.notion.com:443 using the cached (expired) access token
[ERROR] [rust:rmcp::transport::worker] worker quit with fatal: Transport channel closed,
when Client(OAuthChallenge { www_authenticate_header:
"...OAuth, resource_metadata=\".../.well-known/oauth-protected-resource/mcp\",
error=\"invalid_token\", error_description=\"Mis...\"" })
[ERROR] Server notion requires authentication, initiating OAuth flow
[ERROR] OAuth authentication required for notion
The runtime then reads the cached OAuth entry from Keychain but still ends at "OAuth authentication required."
Evidence that no refresh was attempted
Grep of the entire session-start process log:
POSTs to mcp.notion.com/token : 0
Zero refresh_token grants were sent, despite a valid refresh token being cached.
Expected behavior
On a 401 / WWW-Authenticate: … error="invalid_token" for a server that has a cached refresh token:
- Silently POST
grant_type=refresh_token(+client_id) to the discoveredtoken_endpoint. - Persist the rotated access/refresh tokens.
- Reconnect and load the server's tools.
- Only fall back to the interactive OAuth flow if the refresh itself fails (
invalid_grant, expired/revoked).
Impact
- Any remote OAuth MCP server silently loses its tools once the access token expires, until a manual re-auth — even though the refresh token is valid and the whole point of storing it is to avoid exactly this.
- Breaks unattended/scheduled sessions (e.g. a daily automation), where the interactive prompt can never be completed.
Likely root cause / where to look
- The
rmcpstreamable transport surfaces the OAuth challenge as a fatal transport error ("worker quit with fatal …OAuthChallenge"), and the CLI's handler maps "auth required" directly to the interactive flow without first attempting a token refresh. - Secondary: the session's tool list appears to be snapshotted at startup, so even a later successful (re)auth doesn't surface the tools into an already-running session.
Suggested fix
- Insert a refresh step in the auth-challenge path: if a
refresh_tokenis cached for the server, perform the refresh grant against the discoveredtoken_endpoint, persist rotated tokens, and reconnect before considering interactive auth. - Optionally refresh proactively when
expiresAtis at/near expiry before the first connect. - Consider re-syncing tools into active sessions after a late auth success.
Workaround
- Manually perform the
refresh_tokengrant against thetoken_endpoint, write the rotated tokens back into the cache; or re-run the CLI's interactive auth for the server before starting the session.
cc @connor4312 (VS Code team)
Filed with assistance from GitHub Copilot CLI.
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với đường dẫn OAuthChallenge của rmcp streamable HTTP transport và CLI handler ghi log "OAuth authentication required." Kiểm tra các token được lưu trong bộ nhớ đệm tại ~/.copilot/mcp-oauth-config/.tokens.json và token_endpoint đã phát hiện, sau đó xác minh rằng một refresh grant lưu các token đã được xoay vòng và kết nối lại với server. Hoàn tất khi các access token hết hạn được làm mới một cách im lặng và các tool của server được tải mà không cần đăng nhập tương tác.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- rust
- Lĩnh vực
- authentication, cli
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 48/100