@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.
Files changed (2) hide show
  1. package/README.md +569 -0
  2. 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
+ }