Authentication Flow
Hướng dẫn tích hợp Auth0 để lấy JWT token và gọi API.
Tổng quan
Cohost sử dụng Auth0 làm identity provider. FE cần:
- Tích hợp Auth0 SDK
- Lấy JWT token sau khi user đăng nhập
- Gắn token vào header khi gọi API
Auth0 Configuration
Environment Variables
NEXT_PUBLIC_AUTH0_DOMAIN=your-tenant.auth0.com
NEXT_PUBLIC_AUTH0_CLIENT_ID=your-client-id
NEXT_PUBLIC_AUTH0_AUDIENCE=https://api.cohost.vn
Auth0 Provider Setup
// cohost-fe/app/providers.tsx
'use client';
import { Auth0Provider } from '@auth0/auth0-react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<Auth0Provider
domain={process.env.NEXT_PUBLIC_AUTH0_DOMAIN!}
clientId={process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID!}
authorizationParams={{
redirect_uri: window.location.origin,
audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE,
scope: 'openid profile email',
}}
>
{children}
</Auth0Provider>
);
}
Lấy Access Token
Sử dụng Auth0 Hook
// hooks/useAuth.ts
import { useAuth0 } from '@auth0/auth0-react';
export function useAccessToken() {
const { getAccessTokenSilently, isAuthenticated, isLoading } = useAuth0();
const getToken = async () => {
if (!isAuthenticated) {
throw new Error('User not authenticated');
}
return await getAccessTokenSilently();
};
return { getToken, isAuthenticated, isLoading };
}
Sử dụng trong API Calls
import { useAccessToken } from '@/hooks/useAuth';
import { useGetListingsApiV1ListingsGet } from '@/generated/api/listings/listings';
function ListingPage() {
const { getToken, isAuthenticated } = useAccessToken();
const { data } = useGetListingsApiV1ListingsGet(
{},
{
// Thêm token vào header
fetchOptions: async (fetchOptions) => {
const token = await getToken();
return {
...fetchOptions,
headers: {
...fetchOptions.headers,
Authorization: `Bearer ${token}`,
'X-Team-ID': 'team-id-here',
},
};
},
}
);
return <div>{/* ... */}</div>;
}
Interceptor Approach (Khuyến nghị)
Thay vì thủ công thêm token vào mỗi call, sử dụng Axios interceptor:
// lib/api-client.ts
import axios from 'axios';
import { useAuth0 } from '@auth0/auth0-react';
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
});
// Interceptor để thêm token
apiClient.interceptors.request.use(async (config) => {
const { getAccessTokenSilently, isAuthenticated } = useAuth0();
if (isAuthenticated) {
const token = await getAccessTokenSilently();
config.headers.Authorization = `Bearer ${token}`;
}
// Lấy team_id từ context hoặc store
const teamId = getCurrentTeamId();
if (teamId) {
config.headers['X-Team-ID'] = teamId;
}
return config;
});
export default apiClient;
X-Team-ID Header
Hầu hết Internal API yêu cầu header X-Team-ID để xác định team context:
X-Team-ID: team_abc123
Cách lấy Team ID
- Từ URL:
/team/[teamId]/listings - Từ Auth0 claims: Custom claim trong JWT
- Từ User Profile API: Gọi
GET /api/v1/auth/me
// Lấy team_id từ user profile
const { data } = await getCurrentUserInfoApiV1AuthMeGet({
headers: { Authorization: `Bearer ${token}` }
});
const teamId = data?.teams?.[0]?.id;
Auth Flow Diagram
┌──────────┐ ┌──────────┐ ┌──────────┐
│ User │────▶│ Auth0 │────▶│ FE │
│ Login │ │ Login │ │ Get Token│
└──────────┘ └──────────┘ └────┬─────┘
│
▼
┌──────────────┐
│ Cohost API │
│ + Token │
│ + X-Team-ID │
└──────────────┘
Logout
const { logout } = useAuth0();
function LogoutButton() {
return (
<button
onClick={() =>
logout({
logoutParams: {
returnTo: window.location.origin,
},
})
}
>
Logout
</button>
);
}
Error Handling
| Error Code | Meaning | Action |
|---|---|---|
| 401 | Token hết hạn | Refresh token hoặc redirect to login |
| 403 | Không đủ quyền | Hiển thị thông báo lỗi |
| 429 | Rate limited | Thử lại sau |
// Xử lý 401 - Token expired
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Redirect to login hoặc refresh token
window.location.href = '/login';
}
return Promise.reject(error);
}
);
Tiếp theo
- Common Patterns: Pattern thường dùng
- Code Examples: Ví dụ code chi tiết