@rqdhw3n/react-auth-flow 1.0.0
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 +569 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,569 @@
|
|
|
1
|
+
# @rqdhw3n/react-auth-flow
|
|
2
|
+
|
|
3
|
+
A production-ready, TypeScript-first authentication flow package for React applications. Provides a complete solution for handling user authentication with minimal configuration while maintaining flexibility.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Secure by default**: HttpOnly cookie JWT support
|
|
8
|
+
- ⚡ **Zero dependencies**: Uses native fetch API
|
|
9
|
+
- 🎣 **Custom hooks**: `useAuth()` hook for accessing auth state
|
|
10
|
+
- 🔄 **Auto-refresh**: Automatic session refresh capability
|
|
11
|
+
- 🎨 **Unstyled components**: Ready-to-use forms with full customization
|
|
12
|
+
- 🛣️ **Protected routes**: React Router v6 compatible route protection
|
|
13
|
+
- 📦 **TypeScript support**: Full type safety throughout
|
|
14
|
+
- 🔌 **Customizable**: Custom headers, endpoints, and request adapters
|
|
15
|
+
- 🚀 **SSR-friendly**: Works with server-side rendering when configured properly
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @rqdhw3n/react-auth-flow react react-dom react-router-dom
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
or with yarn:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add @rqdhw3n/react-auth-flow react react-dom react-router-dom
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Setup
|
|
30
|
+
|
|
31
|
+
### 1. Wrap your app with AuthProvider
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { AuthProvider } from '@rqdhw3n/react-auth-flow';
|
|
35
|
+
|
|
36
|
+
function App() {
|
|
37
|
+
return (
|
|
38
|
+
<AuthProvider
|
|
39
|
+
baseURL="https://api.example.com"
|
|
40
|
+
autoRefresh={true}
|
|
41
|
+
refreshInterval={5 * 60 * 1000} // 5 minutes
|
|
42
|
+
>
|
|
43
|
+
{/* Your app components */}
|
|
44
|
+
</AuthProvider>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Use the useAuth hook
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { useAuth } from '@rqdhw3n/react-auth-flow';
|
|
53
|
+
|
|
54
|
+
function Header() {
|
|
55
|
+
const { user, isAuthenticated, logout } = useAuth();
|
|
56
|
+
|
|
57
|
+
if (!isAuthenticated) {
|
|
58
|
+
return <span>Please log in</span>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div>
|
|
63
|
+
<p>Welcome, {user?.name}</p>
|
|
64
|
+
<button onClick={logout}>Logout</button>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Usage Examples
|
|
71
|
+
|
|
72
|
+
### LoginForm Component
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
import { LoginForm } from '@rqdhw3n/react-auth-flow';
|
|
76
|
+
import { useNavigate } from 'react-router-dom';
|
|
77
|
+
|
|
78
|
+
function LoginPage() {
|
|
79
|
+
const navigate = useNavigate();
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<LoginForm
|
|
83
|
+
className="my-login-form"
|
|
84
|
+
labels={{
|
|
85
|
+
email: 'Email Address',
|
|
86
|
+
password: 'Password',
|
|
87
|
+
rememberMe: 'Keep me signed in',
|
|
88
|
+
}}
|
|
89
|
+
placeholders={{
|
|
90
|
+
email: 'name@example.com',
|
|
91
|
+
password: 'Enter your password',
|
|
92
|
+
}}
|
|
93
|
+
submitButtonText="Sign In"
|
|
94
|
+
onSuccess={(user) => {
|
|
95
|
+
console.log('Logged in:', user);
|
|
96
|
+
navigate('/dashboard');
|
|
97
|
+
}}
|
|
98
|
+
onError={(error) => {
|
|
99
|
+
console.error('Login failed:', error);
|
|
100
|
+
}}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### RegisterForm Component
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { RegisterForm } from '@rqdhw3n/react-auth-flow';
|
|
110
|
+
|
|
111
|
+
function SignUpPage() {
|
|
112
|
+
return (
|
|
113
|
+
<RegisterForm
|
|
114
|
+
submitButtonText="Create Account"
|
|
115
|
+
labels={{
|
|
116
|
+
name: 'Full Name',
|
|
117
|
+
email: 'Email Address',
|
|
118
|
+
password: 'Password',
|
|
119
|
+
confirmPassword: 'Confirm Password',
|
|
120
|
+
}}
|
|
121
|
+
onSuccess={() => {
|
|
122
|
+
// Navigate to email verification or login
|
|
123
|
+
}}
|
|
124
|
+
onError={(error) => {
|
|
125
|
+
alert(error.message);
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### ForgotPasswordForm Component
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
import { ForgotPasswordForm } from '@rqdhw3n/react-auth-flow';
|
|
136
|
+
|
|
137
|
+
function ForgotPasswordPage() {
|
|
138
|
+
return (
|
|
139
|
+
<ForgotPasswordForm
|
|
140
|
+
submitButtonText="Send Recovery Email"
|
|
141
|
+
labels={{
|
|
142
|
+
email: 'Enter your email address',
|
|
143
|
+
}}
|
|
144
|
+
onSuccess={() => {
|
|
145
|
+
alert('Recovery email sent! Check your inbox.');
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ResetPasswordForm Component
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import { ResetPasswordForm } from '@rqdhw3n/react-auth-flow';
|
|
156
|
+
import { useSearchParams } from 'react-router-dom';
|
|
157
|
+
|
|
158
|
+
function ResetPasswordPage() {
|
|
159
|
+
const [searchParams] = useSearchParams();
|
|
160
|
+
const token = searchParams.get('token') || '';
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<ResetPasswordForm
|
|
164
|
+
token={token}
|
|
165
|
+
submitButtonText="Update Password"
|
|
166
|
+
onSuccess={() => {
|
|
167
|
+
alert('Password updated successfully!');
|
|
168
|
+
// Navigate to login
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### VerifyEmailForm Component
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { VerifyEmailForm } from '@rqdhw3n/react-auth-flow';
|
|
179
|
+
import { useSearchParams } from 'react-router-dom';
|
|
180
|
+
|
|
181
|
+
function VerifyEmailPage() {
|
|
182
|
+
const [searchParams] = useSearchParams();
|
|
183
|
+
const token = searchParams.get('token') || '';
|
|
184
|
+
const email = searchParams.get('email') || '';
|
|
185
|
+
|
|
186
|
+
return (
|
|
187
|
+
<VerifyEmailForm
|
|
188
|
+
token={token}
|
|
189
|
+
email={email}
|
|
190
|
+
onSuccess={() => {
|
|
191
|
+
alert('Email verified!');
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### ProtectedRoute Component
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
import { ProtectedRoute } from '@rqdhw3n/react-auth-flow';
|
|
202
|
+
import { Routes, Route } from 'react-router-dom';
|
|
203
|
+
|
|
204
|
+
function App() {
|
|
205
|
+
return (
|
|
206
|
+
<Routes>
|
|
207
|
+
<Route path="/login" element={<LoginPage />} />
|
|
208
|
+
<Route
|
|
209
|
+
path="/dashboard"
|
|
210
|
+
element={
|
|
211
|
+
<ProtectedRoute redirectTo="/login">
|
|
212
|
+
<Dashboard />
|
|
213
|
+
</ProtectedRoute>
|
|
214
|
+
}
|
|
215
|
+
/>
|
|
216
|
+
<Route
|
|
217
|
+
path="/admin"
|
|
218
|
+
element={
|
|
219
|
+
<ProtectedRoute
|
|
220
|
+
roles={['admin']}
|
|
221
|
+
redirectTo="/unauthorized"
|
|
222
|
+
>
|
|
223
|
+
<AdminPanel />
|
|
224
|
+
</ProtectedRoute>
|
|
225
|
+
}
|
|
226
|
+
/>
|
|
227
|
+
<Route
|
|
228
|
+
path="/manage-users"
|
|
229
|
+
element={
|
|
230
|
+
<ProtectedRoute
|
|
231
|
+
permissions={['users.manage', 'users.delete']}
|
|
232
|
+
redirectTo="/unauthorized"
|
|
233
|
+
>
|
|
234
|
+
<UserManagement />
|
|
235
|
+
</ProtectedRoute>
|
|
236
|
+
}
|
|
237
|
+
/>
|
|
238
|
+
</Routes>
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### useAuth Hook
|
|
244
|
+
|
|
245
|
+
```tsx
|
|
246
|
+
import { useAuth } from '@rqdhw3n/react-auth-flow';
|
|
247
|
+
|
|
248
|
+
function UserProfile() {
|
|
249
|
+
const {
|
|
250
|
+
user,
|
|
251
|
+
isAuthenticated,
|
|
252
|
+
isLoading,
|
|
253
|
+
error,
|
|
254
|
+
login,
|
|
255
|
+
logout,
|
|
256
|
+
refreshSession,
|
|
257
|
+
setUser,
|
|
258
|
+
} = useAuth();
|
|
259
|
+
|
|
260
|
+
const handleLogin = async () => {
|
|
261
|
+
try {
|
|
262
|
+
const user = await login({
|
|
263
|
+
email: 'user@example.com',
|
|
264
|
+
password: 'password',
|
|
265
|
+
rememberMe: true,
|
|
266
|
+
});
|
|
267
|
+
console.log('Logged in as:', user);
|
|
268
|
+
} catch (err) {
|
|
269
|
+
console.error('Login failed:', err);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const handleLogout = async () => {
|
|
274
|
+
await logout();
|
|
275
|
+
console.log('Logged out');
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
if (isLoading) {
|
|
279
|
+
return <div>Loading...</div>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (error) {
|
|
283
|
+
return <div>Error: {error.message}</div>;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (!isAuthenticated) {
|
|
287
|
+
return <button onClick={handleLogin}>Login</button>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div>
|
|
292
|
+
<h1>Hello, {user?.name}</h1>
|
|
293
|
+
<p>Email: {user?.email}</p>
|
|
294
|
+
<p>Roles: {user?.roles?.join(', ')}</p>
|
|
295
|
+
<button onClick={handleLogout}>Logout</button>
|
|
296
|
+
<button onClick={refreshSession}>Refresh Session</button>
|
|
297
|
+
</div>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## API Endpoints Configuration
|
|
303
|
+
|
|
304
|
+
The package expects your backend to provide the following endpoints by default:
|
|
305
|
+
|
|
306
|
+
### Default Endpoints
|
|
307
|
+
|
|
308
|
+
- `POST /auth/login` - Login with email and password
|
|
309
|
+
- `POST /auth/register` - Register a new account
|
|
310
|
+
- `POST /auth/logout` - Logout the user
|
|
311
|
+
- `GET /auth/me` - Get current user information
|
|
312
|
+
- `POST /auth/refresh` - Refresh authentication token
|
|
313
|
+
- `POST /auth/forgot-password` - Request password reset
|
|
314
|
+
- `POST /auth/reset-password` - Reset password with token
|
|
315
|
+
- `POST /auth/verify-email` - Verify email with token
|
|
316
|
+
|
|
317
|
+
### Customizing Endpoints
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { AuthProvider } from '@rqdhw3n/react-auth-flow';
|
|
321
|
+
|
|
322
|
+
<AuthProvider
|
|
323
|
+
baseURL="https://api.example.com"
|
|
324
|
+
endpoints={{
|
|
325
|
+
login: '/auth/signin',
|
|
326
|
+
register: '/auth/signup',
|
|
327
|
+
me: '/user/profile',
|
|
328
|
+
// ... other endpoints
|
|
329
|
+
}}
|
|
330
|
+
>
|
|
331
|
+
{/* Your app */}
|
|
332
|
+
</AuthProvider>
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Expected API Response Formats
|
|
336
|
+
|
|
337
|
+
### Login Response
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"user": {
|
|
342
|
+
"id": 1,
|
|
343
|
+
"name": "John Doe",
|
|
344
|
+
"email": "john@example.com",
|
|
345
|
+
"roles": ["admin"],
|
|
346
|
+
"permissions": ["users.manage", "users.delete"]
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Current User (Me) Response
|
|
352
|
+
|
|
353
|
+
```json
|
|
354
|
+
{
|
|
355
|
+
"user": {
|
|
356
|
+
"id": 1,
|
|
357
|
+
"name": "John Doe",
|
|
358
|
+
"email": "john@example.com",
|
|
359
|
+
"roles": ["admin"],
|
|
360
|
+
"permissions": ["users.manage", "users.delete"]
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Error Response
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"error": {
|
|
370
|
+
"code": "INVALID_CREDENTIALS",
|
|
371
|
+
"message": "Invalid email or password",
|
|
372
|
+
"statusCode": 401
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
## HttpOnly Cookie JWT Authentication
|
|
378
|
+
|
|
379
|
+
For enhanced security, configure your backend to use HttpOnly cookies:
|
|
380
|
+
|
|
381
|
+
```tsx
|
|
382
|
+
// Backend should set a cookie like:
|
|
383
|
+
// Set-Cookie: jwt=token; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
|
|
384
|
+
|
|
385
|
+
// The package will automatically include cookies with requests
|
|
386
|
+
// because credentials: "include" is set by default
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Custom Headers and Request Adapter
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
import { createAuthClient } from '@rqdhw3n/react-auth-flow';
|
|
393
|
+
|
|
394
|
+
// Create a custom client
|
|
395
|
+
const authClient = createAuthClient({
|
|
396
|
+
baseURL: 'https://api.example.com',
|
|
397
|
+
headers: {
|
|
398
|
+
'X-API-Key': 'your-api-key',
|
|
399
|
+
'X-Client-Version': '1.0.0',
|
|
400
|
+
},
|
|
401
|
+
credentials: 'include', // for HttpOnly cookies
|
|
402
|
+
adapter: async (url, options) => {
|
|
403
|
+
// Custom request logic
|
|
404
|
+
console.log('Making request to:', url);
|
|
405
|
+
return fetch(url, options);
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Use in AuthProvider
|
|
410
|
+
<AuthProvider
|
|
411
|
+
baseURL="https://api.example.com"
|
|
412
|
+
// ... other config
|
|
413
|
+
>
|
|
414
|
+
{/* Your app */}
|
|
415
|
+
</AuthProvider>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## TypeScript Customization
|
|
419
|
+
|
|
420
|
+
Extend types for your application:
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
import { AuthUser, AuthContextValue } from '@rqdhw3n/react-auth-flow';
|
|
424
|
+
|
|
425
|
+
// Extend the AuthUser type
|
|
426
|
+
interface AppUser extends AuthUser {
|
|
427
|
+
id: number;
|
|
428
|
+
name: string;
|
|
429
|
+
email: string;
|
|
430
|
+
roles: string[];
|
|
431
|
+
permissions: string[];
|
|
432
|
+
company?: string;
|
|
433
|
+
department?: string;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Use in your components
|
|
437
|
+
import { useAuth } from '@rqdhw3n/react-auth-flow';
|
|
438
|
+
|
|
439
|
+
function MyComponent() {
|
|
440
|
+
const { user } = useAuth();
|
|
441
|
+
const appUser = user as AppUser;
|
|
442
|
+
|
|
443
|
+
return <div>Working in {appUser.department}</div>;
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Styling Components
|
|
448
|
+
|
|
449
|
+
All form components use semantic class names for styling:
|
|
450
|
+
|
|
451
|
+
```css
|
|
452
|
+
.auth-form-group {
|
|
453
|
+
margin-bottom: 1rem;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.auth-form-label {
|
|
457
|
+
display: block;
|
|
458
|
+
margin-bottom: 0.5rem;
|
|
459
|
+
font-weight: 500;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.auth-form-input {
|
|
463
|
+
width: 100%;
|
|
464
|
+
padding: 0.5rem;
|
|
465
|
+
border: 1px solid #ccc;
|
|
466
|
+
border-radius: 4px;
|
|
467
|
+
font-size: 1rem;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.auth-form-input:disabled {
|
|
471
|
+
background-color: #f5f5f5;
|
|
472
|
+
cursor: not-allowed;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.auth-form-error {
|
|
476
|
+
color: #dc3545;
|
|
477
|
+
margin-top: 0.5rem;
|
|
478
|
+
font-size: 0.875rem;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.auth-form-button {
|
|
482
|
+
padding: 0.5rem 1rem;
|
|
483
|
+
border: none;
|
|
484
|
+
border-radius: 4px;
|
|
485
|
+
font-size: 1rem;
|
|
486
|
+
cursor: pointer;
|
|
487
|
+
width: 100%;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.auth-form-button-primary {
|
|
491
|
+
background-color: #007bff;
|
|
492
|
+
color: white;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
.auth-form-button-primary:hover {
|
|
496
|
+
background-color: #0056b3;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.auth-form-button:disabled {
|
|
500
|
+
opacity: 0.5;
|
|
501
|
+
cursor: not-allowed;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.auth-form-checkbox {
|
|
505
|
+
display: flex;
|
|
506
|
+
align-items: center;
|
|
507
|
+
gap: 0.5rem;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.auth-form-checkbox .auth-form-label {
|
|
511
|
+
margin-bottom: 0;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.auth-form-success {
|
|
515
|
+
padding: 1rem;
|
|
516
|
+
background-color: #d4edda;
|
|
517
|
+
border: 1px solid #c3e6cb;
|
|
518
|
+
border-radius: 4px;
|
|
519
|
+
color: #155724;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
.auth-form-success-message {
|
|
523
|
+
margin: 0;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.auth-loading {
|
|
527
|
+
text-align: center;
|
|
528
|
+
padding: 2rem;
|
|
529
|
+
font-size: 1rem;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
.auth-forbidden {
|
|
533
|
+
padding: 2rem;
|
|
534
|
+
text-align: center;
|
|
535
|
+
color: #dc3545;
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
## Error Handling
|
|
540
|
+
|
|
541
|
+
The package normalizes all errors to a consistent format:
|
|
542
|
+
|
|
543
|
+
```tsx
|
|
544
|
+
import { useAuth, AuthError } from '@rqdhw3n/react-auth-flow';
|
|
545
|
+
|
|
546
|
+
function MyComponent() {
|
|
547
|
+
const { error } = useAuth();
|
|
548
|
+
|
|
549
|
+
if (error) {
|
|
550
|
+
const authError: AuthError = error;
|
|
551
|
+
console.log({
|
|
552
|
+
code: authError.code, // e.g., "INVALID_CREDENTIALS"
|
|
553
|
+
message: authError.message,
|
|
554
|
+
statusCode: authError.statusCode, // e.g., 401
|
|
555
|
+
details: authError.details, // additional error info
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return <div>Status: {error?.message}</div>;
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
## License
|
|
564
|
+
|
|
565
|
+
MIT
|
|
566
|
+
|
|
567
|
+
## Support
|
|
568
|
+
|
|
569
|
+
For issues and feature requests, please visit the [GitHub repository](https://github.com/rqdhw3n/react-auth-flow).
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rqdhw3n/react-auth-flow",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A reusable authentication flow package for React applications",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.es.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.es.mjs",
|
|
11
|
+
"require": "./dist/index.cjs",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"dev": "vite",
|
|
20
|
+
"build": "tsc && vite build",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "eslint src --ext ts,tsx",
|
|
23
|
+
"test": "vitest",
|
|
24
|
+
"prepublish": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react",
|
|
28
|
+
"authentication",
|
|
29
|
+
"auth-flow",
|
|
30
|
+
"typescript",
|
|
31
|
+
"jwt",
|
|
32
|
+
"login",
|
|
33
|
+
"register"
|
|
34
|
+
],
|
|
35
|
+
"author": "@rqdhw3n",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": "^18.0.0",
|
|
39
|
+
"react-dom": "^18.0.0",
|
|
40
|
+
"react-router-dom": "^6.0.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"react-router-dom": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/react": "^18.2.0",
|
|
49
|
+
"@types/react-dom": "^18.2.0",
|
|
50
|
+
"@types/react-router-dom": "^5.3.0",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
52
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
53
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
54
|
+
"eslint": "^8.0.0",
|
|
55
|
+
"react": "^18.2.0",
|
|
56
|
+
"react-dom": "^18.2.0",
|
|
57
|
+
"react-router-dom": "^6.0.0",
|
|
58
|
+
"typescript": "^5.0.0",
|
|
59
|
+
"vite": "^4.0.0",
|
|
60
|
+
"vitest": "^0.34.0"
|
|
61
|
+
}
|
|
62
|
+
}
|