Skip to main content

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:

  1. Tích hợp Auth0 SDK
  2. Lấy JWT token sau khi user đăng nhập
  3. 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

  1. Từ URL: /team/[teamId]/listings
  2. Từ Auth0 claims: Custom claim trong JWT
  3. 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 CodeMeaningAction
401Token hết hạnRefresh token hoặc redirect to login
403Không đủ quyềnHiển thị thông báo lỗi
429Rate limitedThử 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