OAuth cho MCP client tùy chỉnh
Nếu bạn đang xây dựng MCP client của riêng mình (CLI, SDK, plugin IDE), trang này mô tả cách triển khai OAuth của chúng tôi: các tiêu chuẩn được hỗ trợ, các endpoint, quy tắc redirect_uri và cách debug các lỗi thường gặp. Người dùng Claude.ai và ChatGPT không cần trang này — hãy dùng Quickstart.
Các tiêu chuẩn chúng tôi triển khai
mcp-telegram.com tuân thủ đầy đủ các đặc tả OAuth mà hệ sinh thái MCP dựa vào — không có phần mở rộng độc quyền, không có shared secret nhúng vào client.
- RFC 6749 — OAuth 2.0 Authorization Framework — authorization code grant cơ bản kèm refresh token
- RFC 7591 — Dynamic Client Registration — bất kỳ client nào cũng có thể tự đăng ký tại /oauth/register mà không cần thỏa thuận trước
- RFC 7636 — PKCE (Proof Key for Code Exchange) — challenge S256 là bắt buộc với public client
- RFC 8252 — OAuth 2.0 for Native Apps — redirect URI loopback IP với độ linh hoạt cho port ephemeral
- RFC 8414 — OAuth 2.0 Authorization Server Metadata — tài liệu discovery tại /.well-known/oauth-authorization-server
- RFC 9728 — OAuth 2.0 Protected Resource Metadata — discovery ranh giới của MCP resource
Các endpoint
Authorization code flow với PKCE
Flow chuẩn RFC 6749 §4.1 + RFC 7636. Không có gì bất ngờ — nếu thư viện OAuth của bạn xử lý được "authorization code + PKCE" thì nó sẽ chạy.
Đăng ký client của bạn
POST /oauth/register với { redirect_uris: ["http://127.0.0.1:<port>/callback"], client_name: "tên-client-của-bạn" }. Server trả về client_id + client_secret (secret là tùy chọn với public PKCE client nhưng nhận được cũng không sao).
Tạo cặp PKCE
Tạo một code_verifier ngẫu nhiên dài 43–128 ký tự, sau đó tính code_challenge = BASE64URL(SHA256(code_verifier)). Lưu verifier ở local để dùng cho bước 4.
Mở /oauth/authorize trong trình duyệt
Redirect người dùng đến /oauth/authorize?response_type=code&client_id=...&redirect_uri=...&state=...&code_challenge=...&code_challenge_method=S256. Server sẽ hiển thị mã QR; người dùng quét nó trong ứng dụng Telegram để xác thực.
Nhận authorization code
Sau khi quét QR, server redirect về redirect_uri của bạn với ?code=...&state=.... Hãy kiểm tra tham số state trùng khớp với giá trị bạn đã gửi đi.
Đổi code lấy token
POST /oauth/token với grant_type=authorization_code, code, redirect_uri, code_verifier, client_id. Bạn nhận được access_token (TTL 10 năm — xem Token Lifetime bên dưới) và refresh_token. Dùng access_token làm Authorization: Bearer ... trên các request tới /mcp.
Quy tắc matching redirect_uri
Chúng tôi tuân thủ nghiêm ngặt RFC 8252 §7.3 và §8.4. Thuật toán matching phụ thuộc vào việc URI đã đăng ký có phải là literal IP loopback hay không.
Loopback (http://127.0.0.1 hoặc http://[::1])
Nếu bạn đăng ký URI loopback, redirect_uri tại thời điểm authorize phải khớp chính xác về scheme, host, path và query — nhưng port có thể khác. RFC 8252 §7.3 yêu cầu điều này vì native client gắn vào một port ephemeral do OS cấp, có thể thay đổi giữa các lần chạy.
- Đã đăng ký http://127.0.0.1:36201/callback khớp với http://127.0.0.1:50000/callback tại thời điểm authorize ✅
- Đã đăng ký http://127.0.0.1:36201/callback KHÔNG khớp với http://127.0.0.1:50000/different-path — path phải chính xác
- Đã đăng ký http://[::1]:36201/cb KHÔNG khớp với http://127.0.0.1:36201/cb — loopback IPv4 và IPv6 là hai định danh riêng biệt
HTTPS (https://your-domain.example/...)
Yêu cầu khớp chính xác từng byte, bao gồm path, query và port (RFC 6749 §3.1.2). Không có độ linh hoạt.
localhost (KHÔNG khuyến khích)
Chúng tôi coi http://localhost như một hostname thông thường — yêu cầu khớp chính xác, không có độ linh hoạt cho port. RFC 8252 §8.3 khuyến nghị dùng literal IP (127.0.0.1 / [::1]) thay vì localhost vì việc phân giải DNS phụ thuộc vào cách triển khai.
PKCE là bắt buộc
code_challenge_method=S256 là bắt buộc trong mọi authorize request. Method plain bị từ chối. Nếu bạn bỏ qua hoàn toàn code_challenge, bước trao đổi /token sẽ thất bại tại bước xác minh code_verifier.
Thời gian sống của token
TTL của access_token được đặt dài có chủ ý (10 năm) vì một số MCP client không lưu refresh_token một cách đáng tin cậy. refresh_token vẫn được phát hành và xoay vòng sau mỗi lần refresh (RFC 6819 §5.2.2.3). Người dùng có thể thu hồi toàn bộ token bất cứ lúc nào tại /my/sessions hoặc bằng cách đăng xuất khỏi Telegram. Người dùng cũng có thể POST tường minh tới /oauth/revoke (RFC 7009) để kết thúc một session.
Các lỗi thường gặp
HTTP 400 "Invalid redirect_uri" tại /oauth/authorize
Tham số query redirect_uri không khớp với bất kỳ URI nào đã đăng ký cho client_id này. Với loopback client: hãy kiểm tra scheme + host + path + query khớp chính xác (chỉ port là linh hoạt). Với HTTPS client: cần bằng nhau từng byte — chú ý dấu gạch chéo cuối và khác biệt URL-encoding.
HTTP 400 "Unknown client"
client_id không tồn tại trong database của chúng tôi. Có thể bạn đã bỏ qua bước /oauth/register, phản hồi đăng ký bị mất trước khi bạn lưu client_id, hoặc client đã bị người dùng thu hồi qua /my/clients.
Client liên tục hiển thị "Needs Auth"
Nếu client của bạn lưu access_token nhưng không lưu refresh_token, bạn sẽ thấy điều này khi access_token hết hạn. Access token của chúng tôi có TTL 10 năm chính là để giảm thiểu vấn đề này, nhưng nếu client của bạn tự làm token hết hạn ở phía client sau một khoảng thời gian ngắn hơn, bạn cần lưu refresh_token hoặc gọi /oauth/revoke + re-auth theo yêu cầu.
Các client đã kiểm thử
Đã xác nhận tương thích với server của chúng tôi. Nếu bạn xây dựng cái gì đó mới và nó hoạt động, hãy gửi PR để bổ sung vào danh sách này.
- Claude.ai web — qua MCP connector tích hợp sẵn
- ChatGPT Apps — qua MCP connector tích hợp sẵn
- Hermes Agent — CLI MCP client mã nguồn mở (RFC 8252 loopback flow)
- Cursor MCP — plugin IDE (RFC 8252 loopback flow)
- Bất kỳ client nào tuân thủ RFC 6749 + RFC 7591 + RFC 7636 + RFC 8252 đều có thể hoạt động mà không cần thay đổi code ở phía chúng tôi.
Nhiều tài khoản Telegram trên một kết nối OAuth
Kể từ v2.32.0, một kết nối OAuth có thể giữ nhiều danh tính Telegram và chuyển đổi giữa chúng chỉ bằng một lệnh gọi tool — không cần chu kỳ Disconnect/Connect, không cần đăng ký client mới.
Liệt kê các tài khoản đã gắn
Gọi telegram-accounts-list. Bạn sẽ thấy tài khoản primary (đã gắn OAuth) được đánh dấu bằng ⭐ nếu nó đang hoạt động, cùng với mọi tài khoản phụ mà bạn đã thêm.
Gắn thêm một tài khoản
Gọi telegram-accounts-add với một label tùy chọn (ví dụ "testing"). Tool trả về một URL dùng một lần (TTL 10 phút) — mở URL đó trên thiết bị bất kỳ và quét mã QR bằng tài khoản Telegram mà bạn muốn thêm.
Chuyển tài khoản đang hoạt động
Gọi telegram-accounts-switch với identifier=label, @username, account_id, hoặc 'primary'. Lệnh gọi tool telegram-* tiếp theo sẽ sử dụng tài khoản đó ngay lập tức. Không cần reconnect.
Gỡ một tài khoản phụ
Gọi telegram-accounts-remove identifier=… trên một tài khoản phụ. Bản thân tài khoản Telegram KHÔNG bị đăng xuất — chỉ liên kết tại đây bị gỡ bỏ. Tài khoản primary không thể gỡ theo cách này; hãy sử dụng luồng Disconnect của client để đăng xuất hoàn toàn.
Mỗi kết nối OAuth được cô lập: các tài khoản bạn gắn qua Hermes sẽ không nhìn thấy được từ Claude.ai hay ChatGPT, ngay cả khi đó là cùng một danh tính Telegram. Để kết nối các tài khoản khác nhau với các MCP client khác nhau, hãy đăng ký từng client riêng biệt và sử dụng telegram-accounts-add bên trong session của chính nó.