Bỏ qua để đến nội dung

Luồng xác thực (Authentication Flow)

Tài liệu này mô tả luồng gọi API theo quy chuẩn BPMN Identity Flow. Nguồn chuẩn cho hành vi là tài liệu đó và proto/auth/ (attestation, authentication), proto/justo/identity/ (đăng ký, đăng nhập, social). Khi các nguồn này đổi, tài liệu phải được cập nhật theo.

Mọi API dùng JSON với field camelCase. Mỗi response có dạng:

{ "code": 0, "message": "", "payload": { } }
  • code = 0 là thành công. Mã lỗi xem tại Mã lỗi.
  • Mọi bước sau init-session cần header X-Api-Session-Id với session ID được cấp ở bước này.
  • Các bước đăng ký và đăng nhập trả về nextStep (enum Step). Client phải gọi đúng endpoint tương ứng với nextStep, không tự suy đoán thứ tự.
Nhóm Endpoint Mục đích
Thiết bị GET /v1/attestation/get-challenge Lấy challengeJwt (chỉ iOS)
Phiên POST /v1/auth/init-session Trao đổi khóa, tạo session
Email và phone verify-email, verify-otp-email, verify-phone, verify-otp-phone Xác minh email và số điện thoại
Đăng ký register, register-basic-info, create-pin-code Tạo tài khoản mới
Đăng nhập verify-password Xác thực mật khẩu
Social social/nonce, social-login Đăng nhập Google hoặc Apple
Đăng xuất logout Kết thúc session

GET /v1/attestation/get-challenge trả về payload.challengeJwt, hết hạn sau 5 phút. Gửi giá trị này trong attestation.challengeJwt khi gọi init-session.

Android không cần bước này và gửi attestation.androidToken.

POST /v1/auth/init-session

Field Bắt buộc Ghi chú
userId Không Hex. Để trống với luồng chưa có người dùng
clientJwtCert Có Khóa công khai P-256 (JWK) cho factor JWT
clientSensitiveJwtCert Có Chứng chỉ JWT cho dữ liệu nhạy cảm
clientP256Cert Có Khóa công khai P-256 cho ECDH, kèm salt 32 byte
attestation Có platform là IOS hoặc ANDROID, kèm các field tương ứng

Với IOS, attestation cần challengeJwt, xKeyId và xChallengeResponse. Với ANDROID, cần androidToken. deviceUuid dài 1 đến 32 ký tự.

payload.jwtToken là JWT ký Ed25519 bằng preshare key. Claim sessionId là giá trị dùng cho header X-Api-Session-Id. Claim publicKey là khóa ECDH của server (65 byte, P-256 không nén). Claim salt là ciphertext AES-256-GCM (60 byte, gồm nonce, ciphertext và tag).

Bước đầu tiên của mọi luồng là verify-email. Kết quả phụ thuộc vào loại tài khoản của email:

Email thuộc về nextStep sau verify-email Luồng
Chưa có tài khoản STEP_VERIFY_EMAIL_OTP (kèm otpId) 4.1 Đăng ký mới
Tài khoản Justo hiện hữu STEP_VERIFY_PHONE (kèm phone đã che) 4.2 Đăng nhập
Tài khoản Ecommerce cũ STEP_OLD_PASSWORD 4.3 Di chuyển tài khoản cũ

Luôn đọc nextStep từ payload trước khi gọi bước tiếp theo.

Bước Endpoint Body nextStep trả về
1 POST /v1/auth/verify-email email STEP_VERIFY_EMAIL_OTP, kèm otpId (hết hạn sau 5 phút)
2 POST /v1/auth/verify-otp-email otpId, otp STEP_VERIFY_PHONE
3 POST /v1/auth/verify-phone phone (E.164) STEP_VERIFY_PHONE_OTP, kèm otpId. Trả lỗi 2021 nếu số đã thuộc tài khoản khác
4 POST /v1/auth/verify-otp-phone otpId, otp STEP_CREATE_ACCOUNT
5 POST /v1/auth/register password (8 đến 256 ký tự) STEP_BASIC_INFO
6 POST /v1/auth/register-basic-info givenName, paternalSurname, maternalSurname, dateOfBirth (YYYY-MM-DD), gender Dưới 18 tuổi: STEP_COMPLETED (tài khoản Level 1). Từ 18 tuổi: STEP_CREATE_PIN_CODE
7 POST /v1/auth/create-pin-code pinCode STEP_COMPLETED (tài khoản Level 2). Chỉ áp dụng khi bước 6 trả STEP_CREATE_PIN_CODE

Trên thiết bị mới hoặc chưa tin cậy, OTP số điện thoại được xác thực trước, mật khẩu xác thực sau.

Bước Endpoint Body nextStep trả về
1 POST /v1/auth/verify-email email STEP_VERIFY_PHONE, kèm phone đã che
2 POST /v1/auth/verify-phone phone STEP_VERIFY_PHONE_OTP, kèm otpId
3 POST /v1/auth/verify-otp-phone otpId, otp STEP_LOGIN_PASSWORD
4 POST /v1/auth/verify-password password STEP_CREATE_PIN_CODE (Level 2, cần nhập PIN) hoặc STEP_COMPLETED (Level 1)

Áp dụng cho người đã có tài khoản Ecommerce nhưng chưa có tài khoản Justo.

Bước Endpoint Body nextStep trả về
1 POST /v1/auth/verify-email email STEP_OLD_PASSWORD
2 POST /v1/auth/verify-password password (mật khẩu Ecommerce cũ) STEP_VERIFY_PHONE, kèm phone của tài khoản cũ để FE tự điền
3 POST /v1/auth/verify-phone phone (đã được tự điền) STEP_VERIFY_PHONE_OTP, kèm otpId
4 POST /v1/auth/verify-otp-phone otpId, otp STEP_CREATE_PIN_CODE nếu đã có thông tin cơ bản, ngược lại STEP_BASIC_INFO

Sau bước 4, tài khoản Justo Level 1 được kích hoạt. Từ đây, luồng tiếp tục giống bước 6 và 7 của mục 4.1.

  1. POST /v1/auth/social/nonce (không có body). Trả về nonce và expiresAt. Nonce hết hạn sau 10 phút và chỉ dùng được một lần.
  2. Client lấy idToken từ nhà cung cấp.
  3. POST /v1/auth/social-login với provider và idToken. Payload trả về id, displayName, phone và nextStep.
    • Tài khoản social mới: STEP_VERIFY_PHONE. Social không cần tạo mật khẩu.
    • Tài khoản đã liên kết Justo: theo cấp độ, STEP_COMPLETED hoặc STEP_CREATE_PIN_CODE.

Chi tiết các trường hợp biên xem Kiến trúc đăng nhập Google / Apple.

POST /v1/auth/logout với header X-Api-Session-Id. Sau khi thành công, session không còn dùng được.

Giá trị Ý nghĩa
STEP_VERIFY_EMAIL_OTP Nhập OTP gửi tới email
STEP_VERIFY_PHONE Xác nhận số điện thoại để gửi OTP
STEP_VERIFY_PHONE_OTP Nhập OTP gửi tới số điện thoại
STEP_CREATE_ACCOUNT Đã xác minh email và phone, cần tạo mật khẩu
STEP_BASIC_INFO Nhập thông tin cá nhân
STEP_CREATE_PIN_CODE Tạo hoặc nhập mã PIN
STEP_LOGIN_PASSWORD Nhập mật khẩu Justo để đăng nhập
STEP_OLD_PASSWORD Tài khoản Ecommerce cũ, nhập mật khẩu cũ
STEP_COMPLETED Hoàn tất
  • Trạng thái luồng (nextStep) được gia hạn 15 phút sau mỗi lần gọi thành công. Nếu chờ quá lâu, session hết hạn và client phải gọi lại init-session.
  • Session sau khi đăng nhập hoặc đăng ký có hiệu lực 30 ngày.
  • OTP email và OTP phone hết hạn sau 5 phút. OTP hết hạn trả lỗi 2017. Nhập sai quá số lần cho phép trả lỗi 2018.
  • Gọi sai thứ tự (ví dụ register khi nextStep không phải STEP_CREATE_ACCOUNT) trả lỗi 2003.

Danh sách đầy đủ mã lỗi xem tại Mã lỗi.

  • Chỉ gửi khóa công khai. Khóa riêng không rời khỏi thiết bị.
  • Kiểm tra chữ ký và thời hạn của jwtToken trước khi dùng sessionId.
  • Không lưu mật khẩu, PIN hoặc OTP trên thiết bị.

Xem thêm Bảo mật.