Giao diện
Route BFF
Route Nitro mà @tasco/nuxt-layer-base mang theo. Trình duyệt chỉ gọi các route này, không gọi thẳng IAM hay backend. Luồng tổng thể: BFF: /api/** đi đâu và Phiên đăng nhập.
| Method | Đường dẫn | File trong layer | Việc |
|---|---|---|---|
| Mọi method | /api/** | server/api/[...].ts | Chuyển tiếp tới backend, gắn token của phiên |
POST | /auth/login | server/routes/auth/login.post.ts | Đổi tài khoản lấy token, tạo phiên |
GET | /auth/session | server/routes/auth/session.get.ts | Người dùng của phiên hiện tại |
GET | /auth/clients | server/routes/auth/clients.get.ts | Danh sách ứng dụng cho form đăng nhập |
POST | /auth/otp/send | server/routes/auth/otp/send.post.ts | Gửi OTP qua Telegram |
GET | /auth/logout | server/routes/auth/logout.get.ts | Xoá phiên rồi chuyển hướng |
GET /auth/login không phải route server mà là trang Vue app/pages/auth/login.vue — cùng đường dẫn, khác method.
Lỗi do chính các route này ném ra có dạng lỗi của h3: mã HTTP ở statusCode, thông báo tiếng Việt ở statusMessage. Phía client đọc qua AppError (status, message) — xem AppError & phân loại.
/api/**
Request: giữ nguyên method, query, body và header, trừ:
| Phần | Xử lý |
|---|---|
| Đường dẫn | Bỏ tiền tố /api, nối vào apiProxyTarget (bỏ / thừa ở cuối target): /api/orders?page=1 → <target>/orders?page=1 |
authorization | Header client gửi bị xoá. Phiên có token còn hạn → Authorization: Bearer <token> |
cookie | Luôn bị xoá — cookie trình duyệt, kể cả tasco_session, không sang backend |
Response: nguyên trạng status, header và body của backend.
| Tình huống | Kết quả |
|---|---|
Chưa đặt NUXT_API_PROXY_TARGET | 500 apiProxyTarget chưa cấu hình. |
Chưa đăng nhập, hoặc token đã hết hạn (expiresAt) | Chuyển tiếp không kèm Authorization — backend tự trả 401 |
Route cụ thể trong server/api/ của dự án (vd server/api/orders/summary.get.ts) thắng catch-all này.
POST /auth/login
Body (LoginCredentials):
| Field | Kiểu | Bắt buộc | Ý nghĩa |
|---|---|---|---|
username | string | Có | Cắt khoảng trắng hai đầu |
password | string | Có | |
clientId | string | Không | Mã ứng dụng (AuthClientOption.code) |
method | 'google' | 'telegram' | Không | Phương thức OTP; đổi sang mã auth.authenMethod.* khi gửi IAM |
otp | string | Không | Mã OTP |
transactionId | string | Không | Trả về từ POST /auth/otp/send |
Response 200: { user: AuthUser } — người dùng đầy đủ roles và permissions lấy từ userInfo. Đồng thời đặt cookie tasco_session.
| Lỗi | Khi |
|---|---|
400 Thiếu tên đăng nhập hoặc mật khẩu. | Thiếu username (sau khi cắt khoảng trắng) hoặc password |
| 401 kèm thông báo của IAM | IAM trả code khác API000 — sai mật khẩu, sai OTP… |
500 auth.baseURL chưa cấu hình (NUXT_AUTH_BASE_URL). | Chưa cấu hình IAM và không bật mock |
| 500 | IAM không trả access token ở auth.mapping.token |
Mock (NUXT_AUTH_MOCK=true): không gọi IAM, nhận mọi tài khoản và trả:
json
{
"user": {
"id": "mock-user",
"name": "Người dùng <username>",
"email": "<username>@tasco.vn",
"roles": ["admin"],
"permissions": ["/orders/search", "/orders/create", "/orders/update"]
}
}Gọi IAM có timeout 10 giây. BFF tự sinh các field thiết bị IAM yêu cầu (deviceId, deviceUuid…).
GET /auth/session
Response 200: { user: AuthUser | null }. Route không ném lỗi xác thực — mọi trường hợp không có phiên hợp lệ đều trả user: null.
| Tình huống | user |
|---|---|
| Không có cookie, cookie sai hoặc bị sửa | null |
| Phiên có token | Gọi lại userInfo bằng token → người dùng đầy đủ quyền |
| userInfo lỗi (token hết hạn, IAM từ chối) | null |
| Mock, hoặc phiên không có token | Người dùng lưu trong cookie |
Plugin 02.auth gọi route này một lần khi app khởi động để nạp useState('tasco:auth:user').
GET /auth/clients
Response 200: { clients: AuthClientOption[] } — chỉ giữ ứng dụng có đủ id, code, name.
| Tình huống | clients |
|---|---|
auth.endpoints.clients rỗng | [] |
| Mock | Hai ứng dụng mẫu OAP_OCM, OAP_CUS |
Không yêu cầu đăng nhập (form đăng nhập gọi route này).
POST /auth/otp/send
Body: { username: string, password: string, clientId?: string }.
Response 200: { transactionId?: string } — gửi kèm POST /auth/login. Chỉ Telegram cần bước này; Google Authenticator thì nhập mã thẳng.
| Lỗi | Khi |
|---|---|
400 Thiếu tên đăng nhập hoặc mật khẩu. | Thiếu username hoặc password |
400 Chưa cấu hình gửi OTP (auth.endpoints.otpSend). | Endpoint gửi OTP để rỗng |
| 401 kèm thông báo của IAM | IAM từ chối |
Mock trả { transactionId: 'mock-transaction' }.
GET /auth/logout
Query: redirect — đường dẫn quay về sau khi đăng xuất, mặc định /.
Xoá cookie tasco_session rồi trả 302. redirect chỉ nhận đường dẫn nội bộ: phải bắt đầu bằng đúng một /; //evil.com, /\evil.com hay URL tuyệt đối đều thành / (chống open redirect). Đây là điều hướng của trình duyệt, không gọi bằng $fetch — useAuth().logout() làm việc đó.
Cookie tasco_session
| Thuộc tính | Giá trị |
|---|---|
| Nội dung | { user, accessToken?, expiresAt? }. Đăng nhập thật: user chỉ giữ id, name, email, roles và permissions lấy tươi ở /auth/session. Mock: cả người dùng giả, không có token |
| Định dạng | v1.<iv>.<ciphertext>.<tag> (base64url), mã hoá và ký AES-256-GCM bằng khoá suy từ NUXT_SESSION_SECRET |
| Cờ | HttpOnly, SameSite=Lax, Path=/, Secure (trừ khi chạy dev) |
| Thời hạn | 8 giờ; không có refresh token — hết hạn thì đăng nhập lại |
Cookie giới hạn 4KB và access token IAM đã khoảng 2,4KB, nên không thêm dữ liệu vào phiên.
Liên quan
- API của layer —
getTascoSessionvà các tiện ích server dùng trong route của dự án. - Biến môi trường & runtimeConfig — endpoint và mapping IAM.
- Hợp đồng dữ liệu — quy ước response cho route tự viết.