Chuyển tới nội dung chính
Version: 4.1

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.

  1. Ứng dụng gửi yêu cầu panel cấp một vé cho ứng dụng mà nó muốn mở.
  2. Nó mở hitechcloud-<app>://open?server=<host>&ticket=<ticket>.
  3. Ứ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ườngBắt buộcÝ nghĩa
appcóuser, admin, reseller hoặc mail. Cũng chấp nhận scheme đầy đủ (hitechcloud-user).
ttlkhôngSố 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_ipkhôngtrue 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áierrorÝ nghĩa
400invalid_requestBody không phải JSON, hoặc lớn hơn 4 KB.
400invalid_appapp không thuộc bốn giá trị trên.
400invalid_ttlttl không nằm trong khoảng 1 đến 60.
400ip_unavailableCó yêu cầu bind_ip nhưng panel không biết địa chỉ của client.
401Không có Bearer token hợp lệ (câu trả lời API thông thường của panel).
403token_not_allowedToken là token MCP. Chỉ token từ POST /api/login mới được cấp vé.
403impersonation_not_allowedPhiên là quản trị viên đang đăng nhập thay tài khoản.
403suspendedTài khoản bị tạm ngưng.
429rate_limit_exceededHơn 10 vé trong một phút cho tài khoản này (ticket_issue_ratelimit).
503ticket_store_unavailableBộ 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áiBodyÝ 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_ticket và nhật ký hoạt động của tài khoản ghi sign-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/login mớ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ì đặt ticket_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ỳ):

  1. Kiểm tra ứng dụng nhận đã được cài, rồi gọi POST /api/auth/ticket với app là tên của nó. Chỉ gửi bind_ip: true nếu cả hai ứng dụng ở trên thiết bị này và cùng một mạng.
  2. Mở hitechcloud-<app>://open?server=<panel host>&ticket=<ticket> ngay lập tức. Không lưu vé.
  3. 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:

  1. Phân tích ticket theo các giới hạn trong mục 2 của INTEROP.md. Bỏ qua nó trừ khi server là máy chủ đã lưu (INTEROP.md đã quy định như vậy).
  2. Gọi POST https://<server>/api/auth/ticket/redeem với ticket và tên app của chính nó. Không theo chuyển hướng.
  3. Với 200, giữ token là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ý.
  4. Chỉ đặt ticketExchangeSupported thà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é.