@djangocfg/layouts 2.1.10 → 2.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -161
- package/package.json +6 -6
- package/src/components/RedirectPage/RedirectPage.tsx +1 -1
- package/src/index.ts +0 -6
- package/src/layouts/AppLayout/AppLayout.tsx +1 -1
- package/src/layouts/AppLayout/BaseApp.tsx +1 -1
- package/src/layouts/AuthLayout/AuthContext.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthCallback.tsx +1 -1
- package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -1
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
- package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -2
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
- package/src/layouts/_components/UserMenu.tsx +1 -1
- package/src/layouts/index.ts +0 -2
- package/src/snippets/Analytics/useAnalytics.ts +1 -1
- package/src/snippets/index.ts +0 -3
- package/src/auth/README.md +0 -962
- package/src/auth/context/AccountsContext.tsx +0 -240
- package/src/auth/context/AuthContext.tsx +0 -604
- package/src/auth/context/index.ts +0 -4
- package/src/auth/context/types.ts +0 -68
- package/src/auth/hooks/index.ts +0 -17
- package/src/auth/hooks/useAuthForm.ts +0 -332
- package/src/auth/hooks/useAuthGuard.ts +0 -25
- package/src/auth/hooks/useAuthRedirect.ts +0 -51
- package/src/auth/hooks/useAutoAuth.ts +0 -49
- package/src/auth/hooks/useGithubAuth.ts +0 -184
- package/src/auth/hooks/useLocalStorage.ts +0 -214
- package/src/auth/hooks/useProfileCache.ts +0 -146
- package/src/auth/hooks/useSessionStorage.ts +0 -189
- package/src/auth/index.ts +0 -10
- package/src/auth/middlewares/index.ts +0 -1
- package/src/auth/middlewares/proxy.ts +0 -32
- package/src/auth/server.ts +0 -6
- package/src/auth/utils/errors.ts +0 -34
- package/src/auth/utils/index.ts +0 -2
- package/src/auth/utils/validation.ts +0 -14
- package/src/contexts/LeadsContext.tsx +0 -156
- package/src/contexts/NewsletterContext.tsx +0 -263
- package/src/contexts/SupportContext.tsx +0 -256
- package/src/contexts/index.ts +0 -59
- package/src/contexts/knowbase/ChatContext.tsx +0 -174
- package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
- package/src/contexts/knowbase/SessionsContext.tsx +0 -174
- package/src/contexts/knowbase/index.ts +0 -61
- package/src/contexts/payments/BalancesContext.tsx +0 -65
- package/src/contexts/payments/CurrenciesContext.tsx +0 -66
- package/src/contexts/payments/OverviewContext.tsx +0 -174
- package/src/contexts/payments/PaymentsContext.tsx +0 -132
- package/src/contexts/payments/README.md +0 -201
- package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
- package/src/contexts/payments/index.ts +0 -50
- package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
- package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
- package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
- package/src/layouts/PaymentsLayout/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/events.ts +0 -47
- package/src/layouts/PaymentsLayout/index.ts +0 -16
- package/src/layouts/PaymentsLayout/types.ts +0 -6
- package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
- package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
- package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
- package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
- package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
- package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
- package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
- package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
- package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
- package/src/layouts/SupportLayout/README.md +0 -91
- package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
- package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
- package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
- package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
- package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
- package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
- package/src/layouts/SupportLayout/components/index.ts +0 -6
- package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
- package/src/layouts/SupportLayout/context/index.ts +0 -2
- package/src/layouts/SupportLayout/events.ts +0 -33
- package/src/layouts/SupportLayout/hooks/index.ts +0 -2
- package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
- package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
- package/src/layouts/SupportLayout/index.ts +0 -8
- package/src/layouts/SupportLayout/types.ts +0 -21
- package/src/snippets/Chat/ChatUIContext.tsx +0 -110
- package/src/snippets/Chat/ChatWidget.tsx +0 -476
- package/src/snippets/Chat/README.md +0 -122
- package/src/snippets/Chat/components/MessageInput.tsx +0 -124
- package/src/snippets/Chat/components/MessageList.tsx +0 -169
- package/src/snippets/Chat/components/SessionList.tsx +0 -192
- package/src/snippets/Chat/components/index.ts +0 -9
- package/src/snippets/Chat/hooks/index.ts +0 -6
- package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
- package/src/snippets/Chat/index.tsx +0 -45
- package/src/snippets/Chat/types.ts +0 -80
- package/src/snippets/ContactForm/ContactForm.tsx +0 -346
- package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
- package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
- package/src/snippets/ContactForm/ContactPage.tsx +0 -131
- package/src/snippets/ContactForm/dynamic.tsx +0 -55
- package/src/snippets/ContactForm/index.ts +0 -34
- package/src/snippets/ContactForm/types.ts +0 -110
package/src/auth/README.md
DELETED
|
@@ -1,962 +0,0 @@
|
|
|
1
|
-
# 🔐 Auth Module Documentation
|
|
2
|
-
|
|
3
|
-
**Package**: @djangocfg/ui
|
|
4
|
-
**Module**: `/src/auth`
|
|
5
|
-
**Version**: 1.0.0
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 📋 Overview
|
|
10
|
-
|
|
11
|
-
Full-featured authentication module for Next.js applications with:
|
|
12
|
-
- OTP authentication (email/phone)
|
|
13
|
-
- React Context for state management
|
|
14
|
-
- Pre-built UI components
|
|
15
|
-
- Route protection (guards)
|
|
16
|
-
- API/media proxy middleware
|
|
17
|
-
- Full TypeScript support
|
|
18
|
-
|
|
19
|
-
**Architecture**: Context API + React Hooks + UI Components
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## 📁 Module Structure
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
src/auth/
|
|
27
|
-
├── context/
|
|
28
|
-
│ ├── AuthContext.tsx # Main auth context with logic
|
|
29
|
-
│ ├── types.ts # TypeScript types
|
|
30
|
-
│ └── index.ts # Exports
|
|
31
|
-
├── hooks/
|
|
32
|
-
│ ├── useAuthGuard.ts # Route protection
|
|
33
|
-
│ ├── useAuthRedirect.ts # Redirect management
|
|
34
|
-
│ ├── useAutoAuth.ts # Auto-login
|
|
35
|
-
│ ├── useAuthForm.ts # Form state
|
|
36
|
-
│ ├── useLocalStorage.ts # localStorage hook
|
|
37
|
-
│ ├── useSessionStorage.ts # sessionStorage hook
|
|
38
|
-
│ └── index.ts
|
|
39
|
-
├── components/
|
|
40
|
-
│ ├── AuthLayout/ # Complete Auth UI
|
|
41
|
-
│ │ ├── AuthLayout.tsx # Main layout
|
|
42
|
-
│ │ ├── AuthContext.tsx # Local context for form
|
|
43
|
-
│ │ ├── IdentifierForm.tsx # Email/Phone input
|
|
44
|
-
│ │ ├── OTPForm.tsx # OTP verification
|
|
45
|
-
│ │ ├── AuthHelp.tsx # Help component
|
|
46
|
-
│ │ ├── types.ts
|
|
47
|
-
│ │ └── index.ts
|
|
48
|
-
│ ├── AuthDialog.tsx # Modal for auth required
|
|
49
|
-
│ └── index.ts
|
|
50
|
-
├── middlewares/
|
|
51
|
-
│ ├── proxy.ts # Next.js middleware for /api/* and /media/*
|
|
52
|
-
│ └── index.ts
|
|
53
|
-
├── utils/
|
|
54
|
-
│ ├── validation.ts # Email/OTP validation
|
|
55
|
-
│ ├── errors.ts # Error utilities
|
|
56
|
-
│ └── index.ts
|
|
57
|
-
└── index.ts # Main export
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## 🚀 Quick Start
|
|
63
|
-
|
|
64
|
-
### 1. Setup AuthProvider
|
|
65
|
-
|
|
66
|
-
```tsx
|
|
67
|
-
// pages/_app.tsx
|
|
68
|
-
import { AuthProvider } from '@djangocfg/layouts/auth';
|
|
69
|
-
|
|
70
|
-
export default function App({ Component, pageProps }) {
|
|
71
|
-
return (
|
|
72
|
-
<AuthProvider
|
|
73
|
-
config={{
|
|
74
|
-
apiUrl: process.env.NEXT_PUBLIC_API_URL,
|
|
75
|
-
routes: {
|
|
76
|
-
auth: '/auth',
|
|
77
|
-
defaultCallback: '/dashboard',
|
|
78
|
-
defaultAuthCallback: '/auth',
|
|
79
|
-
},
|
|
80
|
-
}}
|
|
81
|
-
>
|
|
82
|
-
<Component {...pageProps} />
|
|
83
|
-
</AuthProvider>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### 2. Create Auth Page
|
|
89
|
-
|
|
90
|
-
```tsx
|
|
91
|
-
// pages/auth.tsx
|
|
92
|
-
import { AuthLayout } from '@djangocfg/layouts/auth';
|
|
93
|
-
|
|
94
|
-
export default function AuthPage() {
|
|
95
|
-
return (
|
|
96
|
-
<AuthLayout
|
|
97
|
-
title="Welcome to Unrealon"
|
|
98
|
-
subtitle="Sign in to continue"
|
|
99
|
-
supportEmail="support@djangocfg.com"
|
|
100
|
-
supportPhone="+1 (555) 123-4567"
|
|
101
|
-
>
|
|
102
|
-
{/* Optional: Custom header/footer */}
|
|
103
|
-
</AuthLayout>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### 3. Protect Routes
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
// pages/dashboard.tsx
|
|
112
|
-
import { useAuthGuard } from '@djangocfg/layouts/auth';
|
|
113
|
-
|
|
114
|
-
export default function Dashboard() {
|
|
115
|
-
const { isAuthenticated, isLoading } = useAuthGuard({
|
|
116
|
-
redirectTo: '/auth',
|
|
117
|
-
requireAuth: true,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
if (isLoading) return <div>Loading...</div>;
|
|
121
|
-
|
|
122
|
-
return <div>Protected Dashboard</div>;
|
|
123
|
-
}
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## 🧩 Core Components
|
|
129
|
-
|
|
130
|
-
### 1. AuthProvider %%PRIORITY:HIGH%%
|
|
131
|
-
|
|
132
|
-
**Purpose**: Main authentication context provider.
|
|
133
|
-
|
|
134
|
-
```tsx
|
|
135
|
-
import { AuthProvider } from '@djangocfg/layouts/auth';
|
|
136
|
-
|
|
137
|
-
<AuthProvider config={authConfig}>
|
|
138
|
-
{children}
|
|
139
|
-
</AuthProvider>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**Config Options**:
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
interface AuthConfig {
|
|
146
|
-
apiUrl?: string; // API base URL
|
|
147
|
-
routes?: {
|
|
148
|
-
auth?: string; // Auth page path (default: /auth)
|
|
149
|
-
defaultCallback?: string; // After login redirect (default: /dashboard)
|
|
150
|
-
defaultAuthCallback?: string; // After logout redirect (default: /auth)
|
|
151
|
-
};
|
|
152
|
-
onLogout?: () => void; // Custom logout handler
|
|
153
|
-
onConfirm?: (options) => Promise<{ confirmed: boolean }>; // Custom confirm dialog
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
**Context Value**:
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
interface AuthContextType {
|
|
161
|
-
// State
|
|
162
|
-
user: UserProfile | null;
|
|
163
|
-
isLoading: boolean;
|
|
164
|
-
isAuthenticated: boolean;
|
|
165
|
-
|
|
166
|
-
// Profile
|
|
167
|
-
loadCurrentProfile: () => Promise<void>;
|
|
168
|
-
checkAuthAndRedirect: () => Promise<void>;
|
|
169
|
-
|
|
170
|
-
// Email Methods
|
|
171
|
-
getSavedEmail: () => string | null;
|
|
172
|
-
saveEmail: (email: string) => void;
|
|
173
|
-
clearSavedEmail: () => void;
|
|
174
|
-
|
|
175
|
-
// Phone Methods
|
|
176
|
-
getSavedPhone: () => string | null;
|
|
177
|
-
savePhone: (phone: string) => void;
|
|
178
|
-
clearSavedPhone: () => void;
|
|
179
|
-
|
|
180
|
-
// OTP Methods
|
|
181
|
-
requestOTP: (identifier: string, channel?: 'email' | 'phone', sourceUrl?: string) => Promise<{
|
|
182
|
-
success: boolean;
|
|
183
|
-
message: string;
|
|
184
|
-
}>;
|
|
185
|
-
verifyOTP: (identifier: string, otpCode: string, channel?: 'email' | 'phone', sourceUrl?: string) => Promise<{
|
|
186
|
-
success: boolean;
|
|
187
|
-
message: string;
|
|
188
|
-
user?: UserProfile;
|
|
189
|
-
}>;
|
|
190
|
-
refreshToken: () => Promise<{ success: boolean; message: string }>;
|
|
191
|
-
logout: () => Promise<void>;
|
|
192
|
-
|
|
193
|
-
// Redirect Methods
|
|
194
|
-
getSavedRedirectUrl: () => string | null;
|
|
195
|
-
saveRedirectUrl: (url: string) => void;
|
|
196
|
-
clearSavedRedirectUrl: () => void;
|
|
197
|
-
getFinalRedirectUrl: () => string;
|
|
198
|
-
useAndClearRedirectUrl: () => string;
|
|
199
|
-
saveCurrentUrlForRedirect: () => void;
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
---
|
|
204
|
-
|
|
205
|
-
### 2. AuthLayout %%PRIORITY:HIGH%%
|
|
206
|
-
|
|
207
|
-
**Purpose**: Pre-built UI for auth page with two-step flow.
|
|
208
|
-
|
|
209
|
-
```tsx
|
|
210
|
-
import { AuthLayout } from '@djangocfg/layouts/auth';
|
|
211
|
-
|
|
212
|
-
<AuthLayout
|
|
213
|
-
title="Welcome Back"
|
|
214
|
-
subtitle="Sign in to your account"
|
|
215
|
-
supportEmail="support@example.com"
|
|
216
|
-
supportPhone="+1 234 567 8900"
|
|
217
|
-
className="custom-class"
|
|
218
|
-
>
|
|
219
|
-
{/* Optional custom content above forms */}
|
|
220
|
-
</AuthLayout>
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**Props**:
|
|
224
|
-
|
|
225
|
-
```typescript
|
|
226
|
-
interface AuthProps {
|
|
227
|
-
title?: string; // Main title
|
|
228
|
-
subtitle?: string; // Subtitle text
|
|
229
|
-
supportEmail?: string; // Support email (shows help)
|
|
230
|
-
supportPhone?: string; // Support phone (shows help)
|
|
231
|
-
className?: string; // Custom CSS class
|
|
232
|
-
children?: React.ReactNode; // Custom content above forms
|
|
233
|
-
}
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
**Features**:
|
|
237
|
-
- ✅ Step 1: Email/Phone input with validation
|
|
238
|
-
- ✅ Step 2: OTP verification (6-digit code)
|
|
239
|
-
- ✅ Auto-redirect after successful auth
|
|
240
|
-
- ✅ Error handling with user-friendly messages
|
|
241
|
-
- ✅ Saved identifier (remembers email/phone)
|
|
242
|
-
- ✅ Support info display
|
|
243
|
-
- ✅ Fully styled with Tailwind CSS
|
|
244
|
-
|
|
245
|
-
**Flow**:
|
|
246
|
-
|
|
247
|
-
```
|
|
248
|
-
1. User enters email/phone
|
|
249
|
-
→ Validation
|
|
250
|
-
→ Save to localStorage
|
|
251
|
-
→ Call AuthContext.requestOTP()
|
|
252
|
-
→ Show success message
|
|
253
|
-
|
|
254
|
-
2. User enters OTP code
|
|
255
|
-
→ Validation (6 digits)
|
|
256
|
-
→ Call AuthContext.verifyOTP()
|
|
257
|
-
→ Save tokens (LocalStorage via @djangocfg/api)
|
|
258
|
-
→ Load user profile
|
|
259
|
-
→ Redirect to dashboard
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
---
|
|
263
|
-
|
|
264
|
-
### 3. AuthDialog %%PRIORITY:MEDIUM%%
|
|
265
|
-
|
|
266
|
-
**Purpose**: Modal dialog for "auth required" scenarios.
|
|
267
|
-
|
|
268
|
-
```tsx
|
|
269
|
-
import { AuthDialog, DIALOG_EVENTS } from '@djangocfg/layouts/auth';
|
|
270
|
-
|
|
271
|
-
// In your app
|
|
272
|
-
<AuthDialog
|
|
273
|
-
authPath="/auth"
|
|
274
|
-
onAuthRequired={() => {
|
|
275
|
-
// Custom handler
|
|
276
|
-
}}
|
|
277
|
-
/>
|
|
278
|
-
|
|
279
|
-
// Trigger from anywhere
|
|
280
|
-
window.dispatchEvent(new CustomEvent(DIALOG_EVENTS.OPEN_AUTH_DIALOG, {
|
|
281
|
-
detail: { message: 'Please sign in to access this feature' }
|
|
282
|
-
}));
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Events**:
|
|
286
|
-
|
|
287
|
-
```typescript
|
|
288
|
-
const DIALOG_EVENTS = {
|
|
289
|
-
OPEN_AUTH_DIALOG: 'OPEN_AUTH_DIALOG', // Open dialog
|
|
290
|
-
CLOSE_AUTH_DIALOG: 'CLOSE_AUTH_DIALOG', // Close dialog
|
|
291
|
-
AUTH_SUCCESS: 'AUTH_SUCCESS', // Auth successful
|
|
292
|
-
AUTH_FAILURE: 'AUTH_FAILURE', // Auth failed
|
|
293
|
-
};
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
**Usage Example**:
|
|
297
|
-
|
|
298
|
-
```tsx
|
|
299
|
-
// In a protected action
|
|
300
|
-
function handleProtectedAction() {
|
|
301
|
-
if (!isAuthenticated) {
|
|
302
|
-
window.dispatchEvent(new CustomEvent(DIALOG_EVENTS.OPEN_AUTH_DIALOG, {
|
|
303
|
-
detail: { message: 'Sign in to save your work' }
|
|
304
|
-
}));
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Continue with action
|
|
309
|
-
}
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
---
|
|
313
|
-
|
|
314
|
-
## 🪝 Hooks
|
|
315
|
-
|
|
316
|
-
### 1. useAuth() %%PRIORITY:HIGH%%
|
|
317
|
-
|
|
318
|
-
**Purpose**: Access AuthContext from any component.
|
|
319
|
-
|
|
320
|
-
```tsx
|
|
321
|
-
import { useAuth } from '@djangocfg/layouts/auth';
|
|
322
|
-
|
|
323
|
-
function MyComponent() {
|
|
324
|
-
const {
|
|
325
|
-
user,
|
|
326
|
-
isAuthenticated,
|
|
327
|
-
isLoading,
|
|
328
|
-
requestOTP,
|
|
329
|
-
verifyOTP,
|
|
330
|
-
logout
|
|
331
|
-
} = useAuth();
|
|
332
|
-
|
|
333
|
-
if (isLoading) return <div>Loading...</div>;
|
|
334
|
-
|
|
335
|
-
if (!isAuthenticated) {
|
|
336
|
-
return <button onClick={() => requestOTP(email)}>Sign In</button>;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
return (
|
|
340
|
-
<div>
|
|
341
|
-
Welcome, {user?.email}
|
|
342
|
-
<button onClick={logout}>Logout</button>
|
|
343
|
-
</div>
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
### 2. useAuthGuard() %%PRIORITY:HIGH%%
|
|
351
|
-
|
|
352
|
-
**Purpose**: Protect routes, auto-redirect unauthorized users.
|
|
353
|
-
|
|
354
|
-
```tsx
|
|
355
|
-
import { useAuthGuard } from '@djangocfg/layouts/auth';
|
|
356
|
-
|
|
357
|
-
function ProtectedPage() {
|
|
358
|
-
const { isAuthenticated, isLoading } = useAuthGuard({
|
|
359
|
-
redirectTo: '/auth', // Where to redirect if not authenticated
|
|
360
|
-
requireAuth: true, // Require authentication (default: true)
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
if (isLoading) return <div>Checking authentication...</div>;
|
|
364
|
-
|
|
365
|
-
return <div>Protected content</div>;
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
**Options**:
|
|
370
|
-
|
|
371
|
-
```typescript
|
|
372
|
-
interface UseAuthGuardOptions {
|
|
373
|
-
redirectTo?: string; // Redirect path (default: '/auth')
|
|
374
|
-
requireAuth?: boolean; // Require auth (default: true)
|
|
375
|
-
}
|
|
376
|
-
```
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
### 3. useAuthRedirectManager() %%PRIORITY:MEDIUM%%
|
|
381
|
-
|
|
382
|
-
**Purpose**: Manage redirect URL after authentication.
|
|
383
|
-
|
|
384
|
-
```tsx
|
|
385
|
-
import { useAuthRedirectManager } from '@djangocfg/layouts/auth';
|
|
386
|
-
|
|
387
|
-
function ProtectedRoute() {
|
|
388
|
-
const {
|
|
389
|
-
setRedirect,
|
|
390
|
-
getFinalRedirectUrl,
|
|
391
|
-
useAndClearRedirect
|
|
392
|
-
} = useAuthRedirectManager({
|
|
393
|
-
fallbackUrl: '/dashboard',
|
|
394
|
-
clearOnUse: true
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Save current URL before redirect to auth
|
|
398
|
-
useEffect(() => {
|
|
399
|
-
if (!isAuthenticated) {
|
|
400
|
-
setRedirect(window.location.pathname);
|
|
401
|
-
router.push('/auth');
|
|
402
|
-
}
|
|
403
|
-
}, [isAuthenticated]);
|
|
404
|
-
|
|
405
|
-
// After successful auth
|
|
406
|
-
const handleLoginSuccess = () => {
|
|
407
|
-
const redirectUrl = useAndClearRedirect();
|
|
408
|
-
router.push(redirectUrl); // Goes back to saved URL or fallback
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
**Methods**:
|
|
414
|
-
|
|
415
|
-
```typescript
|
|
416
|
-
interface AuthRedirectManager {
|
|
417
|
-
redirectUrl: string;
|
|
418
|
-
setRedirect: (url: string) => void;
|
|
419
|
-
getRedirect: () => string;
|
|
420
|
-
clearRedirect: () => void;
|
|
421
|
-
hasRedirect: () => boolean;
|
|
422
|
-
getFinalRedirectUrl: () => string; // Returns redirect or fallback
|
|
423
|
-
useAndClearRedirect: () => string; // Gets and clears redirect
|
|
424
|
-
}
|
|
425
|
-
```
|
|
426
|
-
|
|
427
|
-
---
|
|
428
|
-
|
|
429
|
-
### 4. useAutoAuth() %%PRIORITY:LOW%%
|
|
430
|
-
|
|
431
|
-
**Purpose**: Automatic authentication when tokens exist.
|
|
432
|
-
|
|
433
|
-
```tsx
|
|
434
|
-
import { useAutoAuth } from '@djangocfg/layouts/auth';
|
|
435
|
-
|
|
436
|
-
function App() {
|
|
437
|
-
useAutoAuth({
|
|
438
|
-
enabled: true, // Enable auto-auth
|
|
439
|
-
redirectTo: '/dashboard', // Where to redirect after auto-auth
|
|
440
|
-
onSuccess: (user) => {
|
|
441
|
-
console.log('Auto-auth successful:', user);
|
|
442
|
-
},
|
|
443
|
-
onFailure: () => {
|
|
444
|
-
console.log('Auto-auth failed');
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
return <div>App</div>;
|
|
449
|
-
}
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
---
|
|
453
|
-
|
|
454
|
-
### 5. useLocalStorage() & useSessionStorage() %%PRIORITY:LOW%%
|
|
455
|
-
|
|
456
|
-
**Purpose**: Type-safe storage hooks.
|
|
457
|
-
|
|
458
|
-
```tsx
|
|
459
|
-
import { useLocalStorage, useSessionStorage } from '@djangocfg/layouts/auth';
|
|
460
|
-
|
|
461
|
-
function Component() {
|
|
462
|
-
const [email, setEmail, removeEmail] = useLocalStorage<string>('saved_email', '');
|
|
463
|
-
const [tempData, setTempData] = useSessionStorage<object>('temp_data', {});
|
|
464
|
-
|
|
465
|
-
return (
|
|
466
|
-
<div>
|
|
467
|
-
<input value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
468
|
-
<button onClick={removeEmail}>Clear</button>
|
|
469
|
-
</div>
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
## 🛡️ Middleware
|
|
477
|
-
|
|
478
|
-
### Next.js Proxy Middleware %%PRIORITY:HIGH%%
|
|
479
|
-
|
|
480
|
-
**Purpose**: Proxy `/api/*` and `/media/*` to Django backend.
|
|
481
|
-
|
|
482
|
-
**Setup**:
|
|
483
|
-
|
|
484
|
-
```typescript
|
|
485
|
-
// middleware.ts (in your Next.js app root)
|
|
486
|
-
export { middleware, config } from '@djangocfg/layouts/auth/middlewares';
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
**What it does**:
|
|
490
|
-
|
|
491
|
-
```typescript
|
|
492
|
-
// Proxies these patterns:
|
|
493
|
-
/api/* → NEXT_PUBLIC_API_URL/api/*
|
|
494
|
-
/media/* → NEXT_PUBLIC_API_URL/media/*
|
|
495
|
-
|
|
496
|
-
// Example:
|
|
497
|
-
// Request: http://localhost:3000/api/auth/profile
|
|
498
|
-
// Proxies: http://localhost:8000/api/auth/profile
|
|
499
|
-
```
|
|
500
|
-
|
|
501
|
-
**Why needed**:
|
|
502
|
-
- CORS bypass in development
|
|
503
|
-
- Unified URL structure
|
|
504
|
-
- Transparent access to Django API and media files
|
|
505
|
-
|
|
506
|
-
---
|
|
507
|
-
|
|
508
|
-
## 🔧 Utilities
|
|
509
|
-
|
|
510
|
-
### 1. Validation
|
|
511
|
-
|
|
512
|
-
```typescript
|
|
513
|
-
import { validateEmail, validateOTP } from '@djangocfg/layouts/auth';
|
|
514
|
-
|
|
515
|
-
const isValidEmail = validateEmail('user@example.com'); // true
|
|
516
|
-
const isValidOTP = validateOTP('123456'); // true
|
|
517
|
-
const isInvalidOTP = validateOTP('12345'); // false (not 6 digits)
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
### 2. Error Utilities
|
|
521
|
-
|
|
522
|
-
```typescript
|
|
523
|
-
import { formatAuthError, isNetworkError } from '@djangocfg/layouts/auth/utils';
|
|
524
|
-
|
|
525
|
-
try {
|
|
526
|
-
await authService.login();
|
|
527
|
-
} catch (error) {
|
|
528
|
-
if (isNetworkError(error)) {
|
|
529
|
-
alert('Network error, please try again');
|
|
530
|
-
} else {
|
|
531
|
-
alert(formatAuthError(error));
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
---
|
|
537
|
-
|
|
538
|
-
## 📖 Usage Patterns
|
|
539
|
-
|
|
540
|
-
### Pattern 1: Simple Auth Page
|
|
541
|
-
|
|
542
|
-
```tsx
|
|
543
|
-
// pages/auth.tsx
|
|
544
|
-
import { AuthLayout } from '@djangocfg/layouts/auth';
|
|
545
|
-
|
|
546
|
-
export default function AuthPage() {
|
|
547
|
-
return <AuthLayout />;
|
|
548
|
-
}
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
**That's it!** You get a fully functional auth flow:
|
|
552
|
-
- Email/Phone input
|
|
553
|
-
- OTP verification
|
|
554
|
-
- Auto-redirect
|
|
555
|
-
- Error handling
|
|
556
|
-
- Saved identifier
|
|
557
|
-
|
|
558
|
-
---
|
|
559
|
-
|
|
560
|
-
### Pattern 2: Custom Auth Flow
|
|
561
|
-
|
|
562
|
-
```tsx
|
|
563
|
-
import { useAuth } from '@djangocfg/layouts/auth';
|
|
564
|
-
import { useState } from 'react';
|
|
565
|
-
|
|
566
|
-
export default function CustomAuth() {
|
|
567
|
-
const { requestOTP, verifyOTP } = useAuth();
|
|
568
|
-
const [step, setStep] = useState<'email' | 'otp'>('email');
|
|
569
|
-
const [email, setEmail] = useState('');
|
|
570
|
-
const [otp, setOTP] = useState('');
|
|
571
|
-
|
|
572
|
-
const handleRequestOTP = async () => {
|
|
573
|
-
const result = await requestOTP(email);
|
|
574
|
-
if (result.success) {
|
|
575
|
-
setStep('otp');
|
|
576
|
-
} else {
|
|
577
|
-
alert(result.message);
|
|
578
|
-
}
|
|
579
|
-
};
|
|
580
|
-
|
|
581
|
-
const handleVerifyOTP = async () => {
|
|
582
|
-
const result = await verifyOTP(email, otp);
|
|
583
|
-
if (result.success) {
|
|
584
|
-
// User logged in, tokens saved, will auto-redirect
|
|
585
|
-
} else {
|
|
586
|
-
alert(result.message);
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
if (step === 'email') {
|
|
591
|
-
return (
|
|
592
|
-
<div>
|
|
593
|
-
<input value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
594
|
-
<button onClick={handleRequestOTP}>Send OTP</button>
|
|
595
|
-
</div>
|
|
596
|
-
);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return (
|
|
600
|
-
<div>
|
|
601
|
-
<input value={otp} onChange={(e) => setOTP(e.target.value)} />
|
|
602
|
-
<button onClick={handleVerifyOTP}>Verify</button>
|
|
603
|
-
</div>
|
|
604
|
-
);
|
|
605
|
-
}
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
---
|
|
609
|
-
|
|
610
|
-
### Pattern 3: Protected Route
|
|
611
|
-
|
|
612
|
-
```tsx
|
|
613
|
-
// pages/dashboard.tsx
|
|
614
|
-
import { useAuthGuard } from '@djangocfg/layouts/auth';
|
|
615
|
-
|
|
616
|
-
export default function Dashboard() {
|
|
617
|
-
const { isLoading } = useAuthGuard();
|
|
618
|
-
|
|
619
|
-
if (isLoading) {
|
|
620
|
-
return <div>Loading...</div>;
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
// If not authenticated, already redirected to /auth
|
|
624
|
-
return <div>Welcome to Dashboard</div>;
|
|
625
|
-
}
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
---
|
|
629
|
-
|
|
630
|
-
### Pattern 4: Conditional UI
|
|
631
|
-
|
|
632
|
-
```tsx
|
|
633
|
-
import { useAuth } from '@djangocfg/layouts/auth';
|
|
634
|
-
|
|
635
|
-
function Navigation() {
|
|
636
|
-
const { isAuthenticated, user, logout } = useAuth();
|
|
637
|
-
|
|
638
|
-
if (!isAuthenticated) {
|
|
639
|
-
return <a href="/auth">Sign In</a>;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
return (
|
|
643
|
-
<div>
|
|
644
|
-
<span>Welcome, {user?.email}</span>
|
|
645
|
-
<button onClick={logout}>Logout</button>
|
|
646
|
-
</div>
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
---
|
|
652
|
-
|
|
653
|
-
### Pattern 5: Auth Required Dialog
|
|
654
|
-
|
|
655
|
-
```tsx
|
|
656
|
-
// app.tsx
|
|
657
|
-
import { AuthDialog } from '@djangocfg/layouts/auth';
|
|
658
|
-
|
|
659
|
-
function App() {
|
|
660
|
-
return (
|
|
661
|
-
<>
|
|
662
|
-
<AuthDialog authPath="/auth" />
|
|
663
|
-
{/* Rest of app */}
|
|
664
|
-
</>
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
// anywhere.tsx
|
|
669
|
-
import { DIALOG_EVENTS } from '@djangocfg/layouts/auth';
|
|
670
|
-
|
|
671
|
-
function ProtectedAction() {
|
|
672
|
-
const handleAction = () => {
|
|
673
|
-
if (!isAuthenticated) {
|
|
674
|
-
window.dispatchEvent(new CustomEvent(DIALOG_EVENTS.OPEN_AUTH_DIALOG, {
|
|
675
|
-
detail: { message: 'Sign in to continue' }
|
|
676
|
-
}));
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
// Perform action
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
```
|
|
684
|
-
|
|
685
|
-
---
|
|
686
|
-
|
|
687
|
-
## 🔐 Security Features
|
|
688
|
-
|
|
689
|
-
### 1. Token Management
|
|
690
|
-
|
|
691
|
-
- ✅ Tokens stored in LocalStorage (via @djangocfg/api)
|
|
692
|
-
- ✅ Automatic injection in API requests
|
|
693
|
-
- ✅ Auto-refresh tokens
|
|
694
|
-
- ✅ Secure logout (tokens cleared)
|
|
695
|
-
|
|
696
|
-
### 2. Input Validation
|
|
697
|
-
|
|
698
|
-
- ✅ Email format validation
|
|
699
|
-
- ✅ Phone format validation (libphonenumber-js)
|
|
700
|
-
- ✅ OTP format validation (6 digits)
|
|
701
|
-
- ✅ XSS protection (React automatic)
|
|
702
|
-
|
|
703
|
-
### 3. CSRF Protection
|
|
704
|
-
|
|
705
|
-
- ✅ Django CSRF tokens via cookies
|
|
706
|
-
- ✅ credentials: 'include' in fetch requests
|
|
707
|
-
- ✅ Same-origin policy enforcement
|
|
708
|
-
|
|
709
|
-
### 4. Route Protection
|
|
710
|
-
|
|
711
|
-
- ✅ Server-side middleware (Next.js)
|
|
712
|
-
- ✅ Client-side guards (useAuthGuard)
|
|
713
|
-
- ✅ Auto-redirect unauthorized users
|
|
714
|
-
|
|
715
|
-
---
|
|
716
|
-
|
|
717
|
-
## 🎨 Styling
|
|
718
|
-
|
|
719
|
-
### Tailwind CSS Classes
|
|
720
|
-
|
|
721
|
-
All components use Tailwind CSS:
|
|
722
|
-
|
|
723
|
-
```tsx
|
|
724
|
-
// AuthLayout - Customizable
|
|
725
|
-
<AuthLayout className="bg-gradient-to-br from-blue-500 to-purple-600">
|
|
726
|
-
{/* Custom content */}
|
|
727
|
-
</AuthLayout>
|
|
728
|
-
|
|
729
|
-
// Forms use these classes:
|
|
730
|
-
- bg-background
|
|
731
|
-
- text-foreground
|
|
732
|
-
- border-input
|
|
733
|
-
- text-destructive (errors)
|
|
734
|
-
- text-muted-foreground (help text)
|
|
735
|
-
```
|
|
736
|
-
|
|
737
|
-
### Dark Mode Support
|
|
738
|
-
|
|
739
|
-
```tsx
|
|
740
|
-
// Automatically respects next-themes
|
|
741
|
-
import { ThemeProvider } from 'next-themes';
|
|
742
|
-
|
|
743
|
-
<ThemeProvider attribute="class">
|
|
744
|
-
<AuthProvider>
|
|
745
|
-
<AuthLayout />
|
|
746
|
-
</AuthProvider>
|
|
747
|
-
</ThemeProvider>
|
|
748
|
-
```
|
|
749
|
-
|
|
750
|
-
---
|
|
751
|
-
|
|
752
|
-
## 🧪 Testing
|
|
753
|
-
|
|
754
|
-
### Example: Test Auth Flow
|
|
755
|
-
|
|
756
|
-
```typescript
|
|
757
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
758
|
-
import { AuthProvider, AuthLayout } from '@djangocfg/layouts/auth';
|
|
759
|
-
|
|
760
|
-
test('auth flow works', async () => {
|
|
761
|
-
const { getByPlaceholderText, getByText } = render(
|
|
762
|
-
<AuthProvider>
|
|
763
|
-
<AuthLayout />
|
|
764
|
-
</AuthProvider>
|
|
765
|
-
);
|
|
766
|
-
|
|
767
|
-
// Step 1: Enter email
|
|
768
|
-
const emailInput = getByPlaceholderText('Email or phone');
|
|
769
|
-
fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
|
|
770
|
-
fireEvent.click(getByText('Continue'));
|
|
771
|
-
|
|
772
|
-
// Step 2: Enter OTP
|
|
773
|
-
await waitFor(() => {
|
|
774
|
-
expect(getByText('Enter verification code')).toBeInTheDocument();
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
const otpInput = getByPlaceholderText('000000');
|
|
778
|
-
fireEvent.change(otpInput, { target: { value: '123456' } });
|
|
779
|
-
fireEvent.click(getByText('Verify'));
|
|
780
|
-
|
|
781
|
-
// Expect redirect or success
|
|
782
|
-
await waitFor(() => {
|
|
783
|
-
expect(window.location.pathname).toBe('/dashboard');
|
|
784
|
-
});
|
|
785
|
-
});
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
---
|
|
789
|
-
|
|
790
|
-
## 🐛 Common Issues
|
|
791
|
-
|
|
792
|
-
### Issue 1: "Module not found" error
|
|
793
|
-
|
|
794
|
-
```bash
|
|
795
|
-
# Make sure @djangocfg/api is installed
|
|
796
|
-
pnpm add @djangocfg/api@workspace:*
|
|
797
|
-
```
|
|
798
|
-
|
|
799
|
-
### Issue 2: Infinite redirect loop
|
|
800
|
-
|
|
801
|
-
```typescript
|
|
802
|
-
// Check your routes config
|
|
803
|
-
<AuthProvider config={{
|
|
804
|
-
routes: {
|
|
805
|
-
auth: '/auth', // Auth page path
|
|
806
|
-
defaultCallback: '/dashboard', // After login
|
|
807
|
-
defaultAuthCallback: '/auth' // After logout
|
|
808
|
-
}
|
|
809
|
-
}} />
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
### Issue 3: Tokens not persisting
|
|
813
|
-
|
|
814
|
-
```typescript
|
|
815
|
-
// Check if LocalStorageAdapter is configured in @djangocfg/api
|
|
816
|
-
import { api } from '@djangocfg/api';
|
|
817
|
-
console.log(api.isAuthenticated()); // Should return true after login
|
|
818
|
-
```
|
|
819
|
-
|
|
820
|
-
### Issue 4: CORS errors
|
|
821
|
-
|
|
822
|
-
```typescript
|
|
823
|
-
// Add middleware to proxy /api/*
|
|
824
|
-
// middleware.ts
|
|
825
|
-
export { middleware, config } from '@djangocfg/layouts/auth/middlewares';
|
|
826
|
-
```
|
|
827
|
-
|
|
828
|
-
---
|
|
829
|
-
|
|
830
|
-
## 📊 Architecture Diagram
|
|
831
|
-
|
|
832
|
-
```
|
|
833
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
834
|
-
│ User Browser │
|
|
835
|
-
│ │
|
|
836
|
-
│ ┌──────────────────────────────────────────────────────┐ │
|
|
837
|
-
│ │ Next.js App (Frontend) │ │
|
|
838
|
-
│ │ │ │
|
|
839
|
-
│ │ ┌────────────┐ ┌──────────────┐ ┌─────────────┐ │ │
|
|
840
|
-
│ │ │ AuthLayout │→ │ AuthContext │→ │ @djangocfg/ │ │ │
|
|
841
|
-
│ │ │ (UI) │ │ (State) │ │ api │ │ │
|
|
842
|
-
│ │ └────────────┘ └──────────────┘ └─────────────┘ │ │
|
|
843
|
-
│ │ ↓ ↓ ↓ │ │
|
|
844
|
-
│ │ ┌────────────────────────────────────────────────┐ │ │
|
|
845
|
-
│ │ │ LocalStorage (Tokens) │ │ │
|
|
846
|
-
│ │ └────────────────────────────────────────────────┘ │ │
|
|
847
|
-
│ └──────────────────────────────────────────────────────┘ │
|
|
848
|
-
│ ↓ HTTP │
|
|
849
|
-
└───────────────────────────┼─────────────────────────────────┘
|
|
850
|
-
↓
|
|
851
|
-
┌───────────────┐
|
|
852
|
-
│ Next.js │
|
|
853
|
-
│ Middleware │
|
|
854
|
-
│ (Proxy) │
|
|
855
|
-
└───────┬───────┘
|
|
856
|
-
↓ /api/*, /media/*
|
|
857
|
-
┌───────────────┐
|
|
858
|
-
│ Django API │
|
|
859
|
-
│ Backend │
|
|
860
|
-
└───────────────┘
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
---
|
|
864
|
-
|
|
865
|
-
## 🔗 Related Modules
|
|
866
|
-
|
|
867
|
-
- **@djangocfg/api** - API client with auth services
|
|
868
|
-
- **@djangocfg/ui-nextjs/components** - UI components (Button, Dialog, etc)
|
|
869
|
-
- **@djangocfg/ui-nextjs/hooks** - Utility hooks
|
|
870
|
-
- **next-themes** - Theme support
|
|
871
|
-
- **libphonenumber-js** - Phone validation
|
|
872
|
-
|
|
873
|
-
---
|
|
874
|
-
|
|
875
|
-
## 📝 TODO / Future Improvements
|
|
876
|
-
|
|
877
|
-
- [ ] Add biometric authentication support
|
|
878
|
-
- [ ] Social auth (Google, GitHub, etc)
|
|
879
|
-
- [ ] 2FA/MFA support
|
|
880
|
-
- [ ] Remember device functionality
|
|
881
|
-
- [ ] Session timeout warnings
|
|
882
|
-
- [ ] Magic link authentication
|
|
883
|
-
- [ ] SSR-compatible cookie auth
|
|
884
|
-
- [ ] Rate limiting for OTP requests
|
|
885
|
-
|
|
886
|
-
---
|
|
887
|
-
|
|
888
|
-
## 🎓 Best Practices
|
|
889
|
-
|
|
890
|
-
### 1. Always wrap app in AuthProvider
|
|
891
|
-
|
|
892
|
-
```tsx
|
|
893
|
-
// ✅ Good
|
|
894
|
-
<AuthProvider>
|
|
895
|
-
<App />
|
|
896
|
-
</AuthProvider>
|
|
897
|
-
|
|
898
|
-
// ❌ Bad - hooks won't work
|
|
899
|
-
<App />
|
|
900
|
-
```
|
|
901
|
-
|
|
902
|
-
### 2. Use useAuthGuard for protected routes
|
|
903
|
-
|
|
904
|
-
```tsx
|
|
905
|
-
// ✅ Good - automatic redirect
|
|
906
|
-
function ProtectedPage() {
|
|
907
|
-
useAuthGuard();
|
|
908
|
-
return <div>Content</div>;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
// ❌ Bad - manual checks everywhere
|
|
912
|
-
function ProtectedPage() {
|
|
913
|
-
const { isAuthenticated } = useAuth();
|
|
914
|
-
useEffect(() => {
|
|
915
|
-
if (!isAuthenticated) router.push('/auth');
|
|
916
|
-
}, [isAuthenticated]);
|
|
917
|
-
// ...
|
|
918
|
-
}
|
|
919
|
-
```
|
|
920
|
-
|
|
921
|
-
### 3. Handle loading states
|
|
922
|
-
|
|
923
|
-
```tsx
|
|
924
|
-
// ✅ Good
|
|
925
|
-
function Dashboard() {
|
|
926
|
-
const { isLoading, user } = useAuth();
|
|
927
|
-
if (isLoading) return <Spinner />;
|
|
928
|
-
return <div>Welcome {user?.email}</div>;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// ❌ Bad - might show undefined
|
|
932
|
-
function Dashboard() {
|
|
933
|
-
const { user } = useAuth();
|
|
934
|
-
return <div>Welcome {user.email}</div>; // Error if user is null
|
|
935
|
-
}
|
|
936
|
-
```
|
|
937
|
-
|
|
938
|
-
### 4. Save redirect URL before auth
|
|
939
|
-
|
|
940
|
-
```tsx
|
|
941
|
-
// ✅ Good - user returns to intended page
|
|
942
|
-
function ProtectedRoute() {
|
|
943
|
-
const { setRedirect } = useAuthRedirectManager();
|
|
944
|
-
|
|
945
|
-
useEffect(() => {
|
|
946
|
-
if (!isAuthenticated) {
|
|
947
|
-
setRedirect(window.location.pathname);
|
|
948
|
-
router.push('/auth');
|
|
949
|
-
}
|
|
950
|
-
}, []);
|
|
951
|
-
}
|
|
952
|
-
```
|
|
953
|
-
|
|
954
|
-
---
|
|
955
|
-
|
|
956
|
-
**Version**: 1.0.0
|
|
957
|
-
**Last Updated**: 2025-10-08
|
|
958
|
-
**Maintainer**: Reforms AI
|
|
959
|
-
|
|
960
|
-
---
|
|
961
|
-
|
|
962
|
-
**Tags**: `auth, authentication, otp, react, next.js, context, hooks, ui, middleware`
|