Vé đăng nhập ứng dụng
Một ứng dụng HiTechCloud đã đăng nhập có thể mở một ứng dụng HiTechCloud khác (User, Admin,
Reseller, Mail) ở trạng thái đã đăng nhập, không cần mật khẩu và không đặt
token vào liên kết. Đây là handoff v2 trong docs/INTEROP.md của các ứng dụng di động.
- Ứng dụng gửi yêu cầu panel cấp một vé cho ứng dụng mà nó muốn mở.
- Nó mở
hitechcloud-<app>://open?server=<host>&ticket=<ticket>. - Ứng dụng nhận gửi vé lên panel qua TLS và nhận Bearer token
riêng, y như khi gọi
POST /api/login.
Vé không phải thông tin xác thực để giữ lại. Mặc định nó tồn tại 30 giây (tối đa 60), dùng được một lần, và gắn với tài khoản, ứng dụng đích và máy chủ đã cấp nó.
API là tính năng Enterprise, nên việc cấp vé cần tính năng api trong gói của
tài khoản. Việc đổi vé không cần gì.
Cấp vé
POST /api/auth/ticket
Authorization: Bearer <token from POST /api/login>
Content-Type: application/json
{"app": "user", "ttl": 30, "bind_ip": false}
| Trường | Bắt buộc | Ý nghĩa |
|---|---|---|
app | có | user, admin, reseller hoặc mail. Cũng chấp nhận scheme đầy đủ (hitechcloud-user). |
ttl | không | Số giây vé tồn tại, từ 1 đến 60. Mặc định 30. Giá trị khác trả về 400 invalid_ttl. |
bind_ip | không | true gắn vé với địa chỉ đã yêu cầu cấp nó. Khi đó yêu cầu đổi vé phải đến từ cùng địa chỉ (client IPv6 được tính theo /64). Dùng khi cả hai ứng dụng ở trên một thiết bị và một mạng. |
200:
{
"ticket": "hct1_9f2c...64 hex characters...",
"app": "user",
"expires_in": 30,
"expires_at": 1760000030,
"server": "panel.example.com",
"bound_ip": false
}
Vé có dạng hct1_ theo sau là 64 ký tự hex (69 ký tự), khớp với
^[A-Za-z0-9_-]{16,256}$ của các ứng dụng. Hãy coi nó là chuỗi mờ. Không bao giờ ghi nó vào nhật ký, đưa
vào analytics, hoặc giữ lại sau khi đã mở liên kết.
| Trạng thái | error | Ý nghĩa |
|---|---|---|
| 400 | invalid_request | Body không phải JSON, hoặc lớn hơn 4 KB. |
| 400 | invalid_app | app không thuộc bốn giá trị trên. |
| 400 | invalid_ttl | ttl không nằm trong khoảng 1 đến 60. |
| 400 | ip_unavailable | Có yêu cầu bind_ip nhưng panel không biết địa chỉ của client. |
| 401 | Không có Bearer token hợp lệ (câu trả lời API thông thường của panel). | |
| 403 | token_not_allowed | Token là token MCP. Chỉ token từ POST /api/login mới được cấp vé. |
| 403 | impersonation_not_allowed | Phiên là quản trị viên đang đăng nhập thay tài khoản. |
| 403 | suspended | Tài khoản bị tạm ngưng. |
| 429 | rate_limit_exceeded | Hơn 10 vé trong một phút cho tài khoản này (ticket_issue_ratelimit). |
| 503 | ticket_store_unavailable | Bộ nhớ đệm của panel ngừng hoạt động. |
Mỗi lần cấp đều được ghi vào nhật ký hoạt động của tài khoản
(issued a sign-in ticket for the user app (valid 30s)).
Đổi vé
POST /api/auth/ticket/redeem
Content-Type: application/json
{"ticket": "hct1_...", "app": "user"}
Không có header Authorization, không có cookie. app là tên của chính ứng dụng nhận và
phải là ứng dụng mà vé được cấp cho. twofa_code là tuỳ chọn (xem
bên dưới).
200 là cùng kiểu đăng nhập như POST /api/login, kèm tên tài khoản và
ứng dụng:
{"token": "eyJhbGciOi...", "user_id": 1, "username": "stefan", "app": "user", "expires_in": 3600}
Token là JWT HS256 có hiệu lực một giờ. Không có refresh token: hãy yêu cầu
ứng dụng gửi một vé mới, hoặc đăng nhập. Đổi vé là một lần đăng nhập thực sự: nó được
ghi vào lịch sử đăng nhập và nhật ký hoạt động
(logged in via sign-in ticket for the user app) và kích hoạt thông báo đăng nhập
của tài khoản nếu đang bật.
| Trạng thái | Body | Ý nghĩa |
|---|---|---|
| 400 | {"error":"invalid_request"} | Body không phải JSON, hoặc lớn hơn 4 KB. |
| 400 | {"error":"invalid_app"} | app không thuộc bốn giá trị trên. |
| 401 | {"error":"invalid_ticket"} | Mọi trường hợp từ chối chính vé: sai định dạng, không xác định, hết hạn, đã dùng, cấp cho ứng dụng khác, trên máy chủ khác hoặc (khi có gắn) từ địa chỉ khác, hoặc được cấp trước khi tài khoản bật 2FA. Câu trả lời không bao giờ cho biết trường hợp nào. |
| 401 | {"twofa_required":true,"user_id":1} | Chỉ khi ticket_redeem_require_2fa = yes: gửi twofa_code. Vé được giữ lại. |
| 401 | {"error":"invalid_2fa"} | twofa_code đó sai. Vé được giữ lại, lần thất bại được tính cho tài khoản. |
| 403 | {"error":"suspended"} | Tài khoản bị tạm ngưng. Vé bị huỷ. |
| 403 | {"error":"second_factor_required"} | Chỉ khi ticket_redeem_require_2fa = yes, với tài khoản đăng nhập bằng mã qua email. Hãy đăng nhập bằng mật khẩu trong ứng dụng. |
| 403 | {"error":"...","code":"...","portal_url":"..."} | Tài khoản không được đăng nhập qua cổng này (ví dụ tài khoản chỉ dùng thư trên panel), như với POST /api/login. |
| 429 | {"error":"rate_limit_exceeded",...} | Hơn 10 lần đổi vé một phút từ một địa chỉ client (ticket_ratelimit). |
| 503 | {"error":"ticket_store_unavailable"} | Bộ nhớ đệm của panel ngừng hoạt động. |
Ứng dụng nên coi mọi 401 invalid_ticket là "yêu cầu người dùng đăng nhập" và
không được thử lại cùng vé.
Các quy tắc panel thực thi
- Dùng một lần. Vé được nhận bằng một thao tác xoá nguyên tử, nên trong bất kỳ số
yêu cầu nào mang nó, đúng một yêu cầu đăng nhập được. Lần dùng sau bị từ chối với
invalid_ticketvà nhật ký hoạt động của tài khoản ghisign-in ticket for the user app refused: already used(trong mười phút sau khi vé đã được dùng). - Thời gian sống ngắn. Tối đa 60 giây, được thực thi hai lần: bởi thời hạn của bộ nhớ đệm và bởi thời gian bên trong bản ghi.
- Được gắn. Với tài khoản, ứng dụng đích, host máy chủ (tên host, không kèm cổng, nơi vé được cấp) và, nếu có yêu cầu, địa chỉ client. Vé được đổi cho ứng dụng, host hoặc địa chỉ khác sẽ bị huỷ, không chỉ bị từ chối, và nhật ký hoạt động của tài khoản ghi lý do.
- Lưu dạng băm. Chỉ SHA-256 của vé được giữ, nên một bản sao của bộ nhớ đệm không đăng nhập được cho ai.
- Yếu tố thứ hai. Chỉ token từ
POST /api/loginmới cấp được vé, và token đó đã cần mã 2FA của tài khoản. Tài khoản bật 2FA sau khi vé được cấp sẽ làm vé mất hiệu lực. Nhà vận hành muốn hỏi lại mã khi đổi vé thì đặtticket_redeem_require_2fa = yes. - Tài khoản bị tạm ngưng không thể cấp cũng không thể đổi vé.
- Giới hạn tần suất. Cấp: 10 lần một phút cho mỗi tài khoản. Đổi: 10 lần một phút cho mỗi địa chỉ
client. Cả hai được đặt trong cấu hình panel (
ticket_issue_ratelimit,ticket_ratelimit). - Không dành cho agent. Hai route này không phải công cụ MCP.
Các ứng dụng làm gì
Bên gửi (ứng dụng bất kỳ):
- Kiểm tra ứng dụng nhận đã được cài, rồi gọi
POST /api/auth/ticketvớiapplà tên của nó. Chỉ gửibind_ip: truenếu cả hai ứng dụng ở trên thiết bị này và cùng một mạng. - Mở
hitechcloud-<app>://open?server=<panel host>&ticket=<ticket>ngay lập tức. Không lưu vé. - Nếu lời gọi thất bại (bất kỳ mã nào khác 200), mở liên kết mà không có
ticket. Khi đó bên nhận hiển thị màn hình đăng nhập thông thường với máy chủ và người dùng đã điền sẵn.
Bên nhận:
- Phân tích
tickettheo các giới hạn trong mục 2 của INTEROP.md. Bỏ qua nó trừ khiserverlà máy chủ đã lưu (INTEROP.md đã quy định như vậy). - Gọi
POST https://<server>/api/auth/ticket/redeemvớiticketvà tênappcủa chính nó. Không theo chuyển hướng. - Với
200, giữtokenlàm phiên của ứng dụng và quên vé đi. Với mọi câu trả lời khác, hiển thị màn hình đăng nhập. Không bao giờ ghi vé hay phản hồi vào nhật ký. - Chỉ đặt
ticketExchangeSupportedthành true cho các máy chủ có panel từ 4.0.8 trở lên. Panel phiên bản cũ hơn trả về404, ứng dụng coi đó là "không hỗ trợ".
Cả hai lời gọi đều tới panel nơi tài khoản thuộc về (cổng 2083, hoặc cổng HTTPS mặc định của host khi panel được phục vụ trên đó). API HiTechCloud Admin (2087) của ứng dụng Admin chưa nhận vé.