Giao diện
Form đăng nhập
Trang /auth/login của layer chỉ là vỏ mỏng bọc component <TascoLoginForm> (auto-import). Dự án dùng nguyên, truyền props để đổi nhãn, hoặc thay hẳn trang mà vẫn giữ lại form.
Dùng nguyên
Không làm gì cả — trang đã có sẵn. Nội dung hiển thị điều khiển bằng biến môi trường:
bash
NUXT_PUBLIC_AUTH_METHODS=google,telegram # phương thức xác thực hiện trên form
NUXT_PUBLIC_AUTH_SHOW_CLIENT_SELECT=true # dropdown "Chọn ứng dụng"
# NUXT_PUBLIC_AUTH_DEFAULT_CLIENT_CODE=WP_HRM # cố định một ứng dụng, ẩn dropdownĐổi nhãn và bố cục
vue
<!-- app/pages/auth/login.vue trong dự án — thắng trang của layer -->
<script setup lang="ts">
definePageMeta({ auth: false, layout: false })
const route = useRoute()
</script>
<template>
<div class="login-page">
<TascoLoginForm
title="ONE AUTO"
subtitle="Hệ thống quản lý đơn hàng"
logo="/logo.svg"
:methods="['google']"
:show-client-select="false"
default-client-code="WP_HRM"
@success="navigateTo(String(route.query.redirect ?? '/'))"
/>
</div>
</template>definePageMeta({ auth: false }) là bắt buộc — thiếu nó thì middleware đẩy người dùng vòng lại chính trang đăng nhập.
| Prop | Kiểu | Mặc định | Ý nghĩa |
|---|---|---|---|
title | string | Đăng nhập | Tiêu đề trên form |
subtitle | string | rỗng | Dòng phụ dưới tiêu đề |
logo | string | rỗng | Đường dẫn ảnh logo |
methods | AuthMethod[] | theo public.auth.methods | 'google' và/hoặc 'telegram' |
showClientSelect | boolean | theo public.auth.showClientSelect | Hiện dropdown chọn ứng dụng |
defaultClientCode | string | theo public.auth.defaultClientCode | Mã ứng dụng cố định |
Sự kiện @success phát sau khi đăng nhập thành công; trang tự quyết định đi đâu. Không truyền thì trang mặc định của layer đưa về redirect trong query.
Hai phương thức xác thực
| Phương thức | Cách hoạt động |
|---|---|
| Google Authentication | Người dùng mở app Authenticator lấy mã, nhập thẳng vào ô OTP |
| Telegram | Bấm "Gửi mã" → POST /auth/otp/send → IAM gửi qua Telegram, trả transactionId; form gửi kèm mã này khi đăng nhập. Có đếm ngược 60 giây trước khi gửi lại |
Chỉ bật phương thức mà IAM của dự án thực sự hỗ trợ; bật nhầm thì người dùng gặp lỗi ở bước cuối. Endpoint gửi OTP để rỗng (NUXT_AUTH_ENDPOINTS_OTP_SEND=) sẽ tắt hẳn tính năng này.
Dropdown "Chọn ứng dụng"
IAM định danh ứng dụng bằng mã (WP_HRM), không phải uuid. Form gọi GET /auth/clients (proxy qua BFF để tránh CORS) và chỉ hiện những client có tên.
Dự án chỉ phục vụ một ứng dụng thì tắt dropdown và cố định mã:
bash
NUXT_PUBLIC_AUTH_SHOW_CLIENT_SELECT=false
NUXT_PUBLIC_AUTH_DEFAULT_CLIENT_CODE=WP_HRMĐăng nhập bằng code
Khi tự dựng form hoàn toàn khác:
ts
const { loginWithPassword } = useAuth()
const { $tascoError } = useNuxtApp()
try {
const user = await loginWithPassword({
clientId: 'WP_HRM', // MÃ ứng dụng
username: 'thanhdx',
password: '••••••',
method: 'google', // 'google' | 'telegram'
otp: '123456',
// transactionId: '…' // bắt buộc với Telegram, lấy từ bước gửi OTP
})
await navigateTo('/')
} catch (error) {
// IAM từ chối (sai mật khẩu/OTP) → 401 kèm thông báo tiếng Việt từ IAM.
$tascoError(error)
}loginWithPassword tự cập nhật useAuth().user khi thành công. Không tự gọi /auth/login bằng $fetch — làm vậy thì state người dùng không được cập nhật.
Đăng xuất
ts
const { logout } = useAuth()
await logout('/') // xoá cookie phiên rồi chuyển hướnglogout đi qua GET /auth/logout (điều hướng thật, không phải fetch) để server xoá được cookie httpOnly. Đích chuyển hướng bị kiểm tra: chỉ chấp nhận đường dẫn nội bộ, chặn open redirect.
Thay bằng nhà cung cấp khác
AuthProvider là interface (login, loginWithPassword, logout, handleCallback, refresh, getUser). Layer cung cấp bản dùng form + IAM qua plugin 02.auth. Cắm SSO/SAML khác thì viết plugin cùng tên trong dự án và provide một implement khác — useAuth và mọi trang giữ nguyên.
Việc này đụng tới lõi xác thực, nên mở RFC trước khi làm.
Liên quan
- Phiên đăng nhập — chuyện xảy ra sau khi form thành công.
- Cấu hình IAM — endpoint và cách bóc dữ liệu.
- Chạy dev không có IAM — thử form khi chưa có endpoint thật.