HRMNhân viên

Đăng nhập

Xác thực và đăng nhập vào hệ thống thông qua quy trình 2 bước (thêm bước xác thực 2 lớp khi tài khoản bật 2FA).

Trên Hubport, một tài khoản (email) có thể là nhân viên của nhiều công ty (cửa hàng). Vì vậy đăng nhập gồm 2 bước: bước 1 xác thực email/mật khẩu và lấy danh sách công ty, bước 2 chọn công ty để nhận JWT Token. Mỗi JWT Token chỉ gắn với một công ty; để chuyển sang công ty khác sau khi đăng nhập, dùng Đổi công ty đang làm việc.

Hai API đăng nhập không cần header Authorization.

Bước 1: Khởi tạo đăng nhập (authenticateInit)

Xác thực email/password và lấy danh sách các công ty mà người dùng tham gia.

Endpoint: POST /v1/users/authenticateinit

Body Parameters:

TênKiểuBắt buộcMô tả
emailstringCóEmail người dùng
passwordstringCóMật khẩu

Response Schema:

{
  "total": 2,
  "limit": 20,
  "currentpage": 1,
  "items": [
    {
      "id": 101,
      "user_id": 1,
      "company": {
        "user_id": 1,
        "id": 10311,
        "name": "Công ty TNHH Phần Mềm Xanh",
        "domain": "phanmemxanh",
        "screenname": "phanmemxanh",
        "status": 1,
        "plan": 2,
        "date_created": 1609459200,
        ...
      },
      "employee": {
        "company_id": 10311,
        "user_id": 1,
        "id": 101,
        "full_name": "Nguyễn Văn A",
        "email": "[email protected]",
        "phone": "0987654321",
        "status": 1,
        ...
      },
      "access": "full"
    },
    {
      "id": 205,
      "user_id": 1,
      "company": {
        "user_id": 9,
        "id": 10500,
        "name": "Chi nhánh Hà Nội",
        "domain": "pmx-hanoi",
        "status": 1,
        ...
      },
      "employee": {
        "company_id": 10500,
        "user_id": 1,
        "id": 205,
        "full_name": "Nguyễn Văn A",
        "status": 1,
        ...
      },
      "access": "blocked"
    }
  ]
}

Response Fields (mỗi phần tử của items):

FieldMô tả
idEmployee ID của người dùng trong công ty
user_idUser ID
companyThông tin công ty
employeeThông tin nhân viên trong công ty
access(Chỉ có trên Hubport SaaS) Trạng thái truy cập công ty: full (đầy đủ), limited (giới hạn tính năng, ví dụ hết thời gian dùng thử), blocked (công ty bị khoá, không đăng nhập được), restorable (công ty đã xoá nhưng còn khôi phục được)

Lưu ý:

  • Danh sách trả về tối đa 20 công ty.
  • Chỉ gồm các công ty người dùng đang là nhân viên; công ty đã xoá người dùng khỏi danh sách nhân viên không xuất hiện (và không đăng nhập được ở bước 2).
  • Nếu người dùng chỉ thuộc 1 công ty, ứng dụng có thể gọi luôn bước 2 với company_id của công ty đó.
  • Email sẽ được chuyển về lowercase trước khi xử lý.

Bước 2: Hoàn tất đăng nhập (authenticateFinish)

Xác thực và lấy JWT token cho công ty đã chọn.

Endpoint: POST /v1/users/authenticatefinish

Body Parameters:

TênKiểuBắt buộcMô tả
emailstringCóEmail người dùng
passwordstringCóMật khẩu
company_idintCóID công ty (từ bước 1)
sourcestringNguồn đăng nhập, dùng để hiển thị trong danh sách phiên đăng nhập: frontend (mặc định), frontend-pos hoặc api. Giá trị khác được ghi nhận là frontend

Response Schema:

{
  "jwt": "0123456789abcdef0123456789abcdef",
  "refresh_token": "a1b2c3d4e5f6...",
  "date_expired": 1641920399,
  "id": 1,
  "email": "[email protected]",
  "full_name": "Nguyễn Văn A",
  "role": "1,100,102,103,110",
  "company": {
    "user_id": 1,
    "id": 10311,
    "name": "Công ty TNHH Phần Mềm Xanh",
    "domain": "phanmemxanh",
    "status": 1,
    "plan": 2,
    "date_created": 1609459200,
    ...
  },
  "employee": {
    "company_id": 10311,
    "user_id": 1,
    "id": 101,
    "full_name": "Nguyễn Văn A",
    "email": "[email protected]",
    "phone": "0987654321",
    "status": 1,
    ...
  },
  "company_list": [
    {
      "id": 101,
      "user_id": 1,
      "company": { "id": 10311, "name": "Công ty TNHH Phần Mềm Xanh", ... },
      "employee": { ... }
    }
  ],
  "access": "full",
  "access_state": "paid"
}

Response Fields:

FieldMô tả
jwtJWT token để sử dụng cho các request tiếp theo (gửi trong header Authorization)
refresh_tokenRefresh token để làm mới JWT trước khi hết hạn (dùng một lần)
date_expiredThời điểm hết hạn của phiên (UNIX timestamp)
idUser ID
emailEmail người dùng
full_nameTên đầy đủ của nhân viên trong công ty
roleDanh sách mã quyền (ID) của nhân viên trong công ty, phân cách bằng dấu phẩy. Chủ công ty có toàn bộ quyền
companyThông tin công ty đang đăng nhập
employeeThông tin nhân viên trong công ty
company_listDanh sách công ty người dùng đang là nhân viên (mỗi phần tử gồm id, user_id, company, employee); không gồm công ty đã xoá người dùng khỏi danh sách nhân viên
access(Chỉ có trên Hubport SaaS) Trạng thái truy cập công ty: full, limited hoặc restorable (xem Bước 1)
access_state(Chỉ có trên Hubport SaaS) Trạng thái gói dịch vụ của công ty, ví dụ trial (dùng thử), paid (đã trả phí), past_due (quá hạn thanh toán, đang trong thời gian ân hạn), free (miễn phí), soft_deleted (đã xoá, có thể khôi phục)

Bước 3 (khi cần): Xác thực 2 lớp

Tài khoản đã bật xác thực 2 lớp (2FA, mã 6 số từ ứng dụng xác thực như Google Authenticator), công ty bắt buộc mọi nhân viên dùng 2FA, hoặc người đăng nhập là chủ shop có kết nối sàn (Shopee / Lazada / TikTok Shop đang hoạt động, Hubport SaaS), thì bước 2 không trả JWT dù mật khẩu đúng. Response là HTTP 401 kèm mã tạm challenge:

{
  "error": ["error_two_factor_required"],
  "detail": {
    "challenge": "Q2hhbGxlbmdlVG9rZW5FeGFtcGxlRXhhbXBsZUFC123",
    "expires_in": 300,
    "purpose": "verify",
    "methods": ["totp", "recovery_code"],
    "company_required": false,
    "marketplace_required": false,
    "required_reason": ""
  }
}
Trường detailMô tả
company_requiredCông ty bắt buộc 2FA cho mọi nhân viên
marketplace_requiredNgười đăng nhập là chủ shop có kết nối sàn đang hoạt động và chưa bật 2FA (chỉ khi purpose = "enroll")
required_reasonLý do phải thiết lập khi purpose = "enroll": company_policy (công ty bắt buộc) hoặc marketplace_owner (chủ shop có kết nối sàn). Rỗng khi purpose = "verify". Ứng dụng nên hiện lời giải thích tương ứng
  • purpose = "verify" (lỗi error_two_factor_required): gửi mã 6 số đang hiện trong ứng dụng xác thực, hoặc một mã khôi phục (dạng xxxxx-xxxxx, mỗi mã dùng một lần).

    Endpoint: POST /v1/users/twofactorchallenge/verify, body {"challenge": "...", "code": "123456"}.

  • purpose = "enroll" (lỗi error_two_factor_enroll_required): công ty bắt buộc 2FA, hoặc chủ shop có kết nối sàn, mà tài khoản chưa bật. Người dùng phải thiết lập trước khi có JWT:

    1. POST /v1/users/twofactorchallenge/enrollinit, body {"challenge": "..."}, trả {"secret", "otpauth_url", "expires_in"}. Hiện otpauth_url dạng mã QR để người dùng quét bằng ứng dụng xác thực.
    2. POST /v1/users/twofactorchallenge/enrollfinish, body {"challenge": "...", "code": "123456"}. Response có thêm recovery_codes (10 mã khôi phục, chỉ trả một lần: ứng dụng phải hiện cho người dùng lưu lại).

Thành công: response giống bước 2 (jwt, refresh_token, company, ...) kèm two_factor_method (totp hoặc recovery_code). Mã challenge hết hạn sau expires_in giây và chỉ dùng được một lần; hết hạn thì gọi lại bước 2. Ba API này không cần header Authorization.

HTTPCodeMô tả
404error_two_factor_challenge_invalidchallenge sai, hết hạn hoặc đã dùng (đăng nhập lại từ bước 2)
409error_two_factor_already_enabled(enroll) Tài khoản đã bật 2FA trong lúc chờ: đăng nhập lại để nhập mã
422error_two_factor_code_invalidMã sai, hoặc mã đã dùng (mã 6 số không dùng lại được trong cùng 30 giây; mã khôi phục chỉ dùng một lần)
422error_two_factor_setup_expired(enroll) Mã QR hết hạn: gọi lại enrollinit
429error_two_factor_too_many_attemptsSai quá 5 lần trong 15 phút

Error Codes

HTTPCodeMô tả
401error_employee_not_foundKhông tìm thấy thông tin nhân viên
401error_two_factor_requiredMật khẩu đúng, cần mã xác thực 2 lớp (xem Bước 3). Response có detail.challenge
401error_two_factor_enroll_requiredMật khẩu đúng, công ty bắt buộc 2FA (hoặc chủ shop có kết nối sàn) mà tài khoản chưa bật (xem Bước 3). Response có detail.challenge, detail.required_reason
403error_company_lockedCông ty đang bị khoá, không đăng nhập được. Response có thêm detail.reason, detail.state (xem HTTP Status Code)
422error_email_requiredThiếu thông tin email
422error_email_format_invalidĐịnh dạng email không hợp lệ
422error_password_emptyThiếu mật khẩu
422error_account_invalidEmail hoặc mật khẩu không đúng. Email chưa có tài khoản và sai mật khẩu trả cùng mã này (không cho biết email nào đã có tài khoản)
422error_company_id_not_foundThiếu hoặc không tìm thấy ID công ty (chỉ ở bước 2)
422error_employee_with_email_not_foundEmail không thuộc nhân viên của công ty này (chỉ ở bước 2, chỉ trả khi email và mật khẩu đúng)
500error_session_can_not_be_savedLỗi tạo phiên làm việc

Mã lỗi đã bỏ

error_account_not_found không còn được trả về (gộp vào error_account_invalid). Ứng dụng tích hợp nên hiển thị cùng một thông báo cho cả hai mã.


Sử dụng JWT Token

Sau khi đăng nhập thành công, sử dụng JWT token trong header của các request tiếp theo (gửi nguyên giá trị, không có tiền tố Bearer):

Authorization: 0123456789abcdef0123456789abcdef

Xem thêm User Login Token.

On this page