Universal Auth
Luồng OTP redirect cho phép app khác mượn phiên đăng nhập admin. KHÔNG phải SSO provider list.
/universalauth là luồng chuyển tiếp xác thực (OTP-based redirect), không phải UI cấu hình SSO provider. Dùng khi external app cần đăng nhập "mượn" phiên admin: app gửi user qua /universalauth với redirect_url + scheme, hệ thống generate OTP rồi redirect về app với OTP đính kèm.
Vị trí menu admin
Không có mục riêng trên menu. Chỉ dùng khi có dự án tích hợp: mở đường dẫn /universalauth kèm tham số do đội kỹ thuật cung cấp.
Đường dẫn hoạt động cả khi chưa đăng nhập: nếu cần, hệ thống chuyển sang trang đăng nhập rồi quay lại đúng liên kết Universal Auth sau khi đăng nhập xong (luồng hash #/universalauth?...).
Người dùng trình duyệt thường vào dạng /#/universalauth?redirect_url=...&scheme=... tùy cách triển khai host admin.
Contract route đã audit
Query params bắt buộc
redirect_url(string) - URL đích sau khi có OTP. Frontend hiện chấp nhận URL bắt đầu bằnghttp://hoặchttps://sau khidecodeURIComponent; production nên ưu tiênhttps://và domain đã được cho phép.scheme(string) - định danh scheme của app khách (sẽ được embed vào URL OTP cuối cùng).
Luồng xử lý
1. User mở /universalauth?redirect_url=...&scheme=...
2. Nếu redirect_url rỗng → hiện ErrorAlert "redirect_url_required" + link đăng nhập (nếu chưa login).
3. Nếu user CHƯA đăng nhập:
- Build returnUrl = `${origin}/#/universalauth?redirect_url=...&scheme=...` (encode lại).
- navigate(`/user/login?return=${btoa(returnUrl)}`, {replace: true}).
- Sau khi login xong, login page redirect về /universalauth → bước 4.
4. Nếu user ĐÃ đăng nhập + redirect_url hợp lệ + scheme hợp lệ:
- Set processing = true, errors = [].
- Gọi AccountRepository.getLoginQrOtp(scheme) → lấy OTP.
- Build redirectUrlWithOtp = `${redirect_url}/otp/${otp}/scheme/${scheme}`.
- window.location.replace(redirectUrlWithOtp) - full page redirect.
5. Có errors → hiện ErrorAlert + nút "Retry" gọi lại doGetLoginQrOtp.useRef initOtpRef đảm bảo không gọi OTP nhiều lần (tránh re-trigger khi component re-render).
Validation messages
| Code | Khi nào |
|---|---|
redirect_url_required | redirectUrl.length === 0 (query không có hoặc rỗng). |
redirect_url_invalid | URL không bắt đầu http:// / https:// sau decode. |
scheme_invalid | Scheme rỗng sau decode. |
I18n prefix user:universalauth.error.
Giao diện
- Header:
<UniversalAuthHeader>. - Body: nếu chưa có redirect_url →
<ErrorAlert items={["redirect_url_required"]}>+ link/login. - Nếu đang lấy OTP →
<Spin>. - Sau khi
window.location.replacemà browser delay → fallback box hiện text "Sau 5 giây nếu trình duyệt không tự động chuyển, vui lòng nhấn link sau:" kèm<a href={fallbackReturnUrl}>để click thủ công. - Nếu có lỗi →
<ErrorAlert>+ nút Retry.
Khi nào dùng
- App mobile/desktop của Cropany (POS, Storefront admin tools...) cần SSO mượn phiên web admin.
- Tích hợp app bên thứ ba được phép đăng nhập user của mình.
Bảo mật
- CHỈ cấu hình
redirect_urlcho domain tin cậy (whitelist app). Backend nên validateredirect_urlthuộc allowlist của tenant - frontend chỉ check format. - Không gửi OTP trong URL qua kênh không an toàn - production nên dùng
https://và domain thuộc allowlist. schemephải là enum cố định ở backend - không cho user truyền tuỳ ý vì có thể bị abuse.
Lưu ý - Lỗi thường gặp
redirect_url_invalidmặc dù URL hợp lệ: kiểm tra encode - URL gốc đãencodeURIComponentđúng chưa. FrontenddecodeURIComponent1 lần trước khi check.- Browser không tự redirect: rare - dùng fallback link text bên dưới.
- Lỗi
scheme_invaliddù truyền scheme: chuỗi bị empty sau decode. Đảm bảo?scheme=xxxkhông phải?scheme=. - Login xong nhưng quay về
/universalauthlại bị redirect login lần 2: cookie session không được set đúng domain. Kiểm tra cookie domain config. - OTP hợp lệ ngắn hạn: chỉ dùng 1 lần. Gọi lại
getLoginQrOtpđể có OTP mới.
Ai được xem và chỉnh?
- Trang không khóa theo một “vai trò menu” riêng: người chưa đăng nhập được chuyển đi đăng nhập trước; sau khi đăng nhập hợp lệ, máy chủ mới cấp OTP qua API.
- Địa chỉ đích (
redirect_url): phải do backend tin cậy (danh sách cho phép); giao diện chỉ kiểm tra dạng URLhttp/https, không thay cho kiểm tra an toàn phía máy chủ.