@skylabs-digital/react-identity-access 2.23.0 โ†’ 2.25.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 CHANGED
@@ -15,6 +15,7 @@ A powerful, modern authentication and authorization library for React applicatio
15
15
  - **๐Ÿ”„ Session Management** - Automatic session handling and token refresh
16
16
  - **๐ŸŽจ Feature Flags** - Built-in feature flag management
17
17
  - **๐Ÿ’ณ Subscription Management** - Integrated billing and subscription handling
18
+ - **๐ŸŽ›๏ธ Fully Customizable Components** - All texts, styles, and icons are overridable via props
18
19
 
19
20
  ## ๐Ÿ“ฆ Installation
20
21
 
@@ -22,21 +23,19 @@ A powerful, modern authentication and authorization library for React applicatio
22
23
  npm install @skylabs-digital/react-identity-access
23
24
  # or
24
25
  yarn add @skylabs-digital/react-identity-access
25
- # or
26
- pnpm add @skylabs-digital/react-identity-access
27
26
  ```
28
27
 
29
28
  ## ๐Ÿƒโ€โ™‚๏ธ Quick Start
30
29
 
31
30
  ### 1. Setup Providers
32
31
 
33
- Wrap your application with the required providers:
32
+ Wrap your application with the required providers in order:
34
33
 
35
34
  ```tsx
36
- import {
37
- AppProvider,
38
- TenantProvider,
39
- AuthProvider
35
+ import {
36
+ AppProvider,
37
+ TenantProvider,
38
+ AuthProvider,
40
39
  } from '@skylabs-digital/react-identity-access';
41
40
 
42
41
  function App() {
@@ -49,7 +48,7 @@ function App() {
49
48
  >
50
49
  <TenantProvider
51
50
  config={{
52
- tenantMode: 'selector', // 'subdomain', 'selector', or 'fixed'
51
+ tenantMode: 'selector', // 'subdomain' | 'selector' | 'fixed' | 'optional'
53
52
  selectorParam: 'tenant',
54
53
  }}
55
54
  >
@@ -68,14 +67,12 @@ function App() {
68
67
  import { useAuth } from '@skylabs-digital/react-identity-access';
69
68
 
70
69
  function LoginComponent() {
71
- const { login, logout, sessionManager } = useAuth();
72
- const user = sessionManager.getUser();
70
+ const { login, logout, currentUser } = useAuth();
73
71
 
74
72
  const handleLogin = async () => {
75
73
  try {
76
74
  // Supports both email and phone number
77
- await login('user@example.com', 'password'); // Email
78
- // await login('+1234567890', 'password'); // Phone
75
+ await login({ username: 'user@example.com', password: 'password' });
79
76
  } catch (error) {
80
77
  console.error('Login failed:', error);
81
78
  }
@@ -83,9 +80,9 @@ function LoginComponent() {
83
80
 
84
81
  return (
85
82
  <div>
86
- {user ? (
83
+ {currentUser ? (
87
84
  <div>
88
- <p>Welcome, {user.name}!</p>
85
+ <p>Welcome, {currentUser.name}!</p>
89
86
  <button onClick={logout}>Logout</button>
90
87
  </div>
91
88
  ) : (
@@ -96,53 +93,22 @@ function LoginComponent() {
96
93
  }
97
94
  ```
98
95
 
99
- ### 3. Magic Link Authentication
96
+ ### 3. Pre-built Login Form
100
97
 
101
98
  ```tsx
102
- import {
103
- MagicLinkForm,
104
- MagicLinkVerify,
105
- useAuth
106
- } from '@skylabs-digital/react-identity-access';
99
+ import { LoginForm } from '@skylabs-digital/react-identity-access';
107
100
  import { useNavigate } from 'react-router-dom';
108
101
 
109
- // Send Magic Link
110
- function MagicLinkLogin() {
102
+ function LoginPage() {
111
103
  const navigate = useNavigate();
112
104
 
113
- const handleSuccess = (response) => {
114
- console.log('Magic link sent successfully!');
115
- };
116
-
117
105
  return (
118
- <MagicLinkForm
119
- frontendUrl="https://yourapp.com"
120
- onSuccess={handleSuccess}
121
- onLoginClick={() => navigate('/login')}
106
+ <LoginForm
107
+ onSuccess={() => navigate('/dashboard')}
108
+ onForgotPassword={() => navigate('/forgot-password')}
122
109
  onSignupClick={() => navigate('/signup')}
123
- />
124
- );
125
- }
126
-
127
- // Verify Magic Link (at /magic-link/verify route)
128
- function MagicLinkVerifyPage() {
129
- const navigate = useNavigate();
130
-
131
- const handleSuccess = (data) => {
132
- console.log('Magic link verified!', data);
133
- navigate('/dashboard');
134
- };
135
-
136
- const handleError = (error) => {
137
- console.error('Verification failed:', error);
138
- };
139
-
140
- return (
141
- <MagicLinkVerify
142
- onSuccess={handleSuccess}
143
- onError={handleError}
144
- onBackToLogin={() => navigate('/login')}
145
- autoRedirectDelay={3000}
110
+ onMagicLinkClick={() => navigate('/magic-link')}
111
+ showMagicLinkOption={true}
146
112
  />
147
113
  );
148
114
  }
@@ -165,117 +131,565 @@ function AdminPanel() {
165
131
  }
166
132
  ```
167
133
 
168
- ## ๐Ÿงฉ Pre-built Components
134
+ ---
169
135
 
170
- The library includes ready-to-use form components with full customization support:
136
+ ## ๐Ÿงฉ Component Reference
171
137
 
172
- ### Authentication Forms
138
+ All form components support three customization axes:
173
139
 
174
- ```tsx
175
- import {
176
- LoginForm,
177
- SignupForm,
178
- MagicLinkForm,
179
- MagicLinkVerify,
180
- PasswordRecoveryForm
181
- } from '@skylabs-digital/react-identity-access';
140
+ - **`copy`** - Override any user-facing text (labels, placeholders, messages, buttons)
141
+ - **`styles`** - Override any inline style on any element (`React.CSSProperties`)
142
+ - **`icons`** - Override SVG icons (where applicable)
143
+
144
+ All customization props are **optional** and **backward compatible** โ€” if you omit them, sensible defaults are used.
145
+
146
+ ---
147
+
148
+ ### LoginForm
149
+
150
+ Traditional email/phone + password login form.
151
+
152
+ #### Props (`LoginFormProps`)
153
+
154
+ | Prop | Type | Default | Description |
155
+ |------|------|---------|-------------|
156
+ | `copy` | `LoginFormCopy` | See below | Override user-facing texts |
157
+ | `styles` | `LoginFormStyles` | See below | Override inline styles |
158
+ | `icons` | `LoginFormIcons` | Eye/EyeOff SVGs | Override password toggle icons |
159
+ | `onSuccess` | `(data: any) => void` | โ€” | Called after successful login |
160
+ | `onError` | `(error: string) => void` | โ€” | Called on login failure |
161
+ | `onForgotPassword` | `() => void` | โ€” | Navigate to forgot password |
162
+ | `onSignupClick` | `() => void` | โ€” | Navigate to signup |
163
+ | `onMagicLinkClick` | `() => void` | โ€” | Navigate to magic link |
164
+ | `showForgotPassword` | `boolean` | `true` | Show "Forgot password?" link |
165
+ | `showSignupLink` | `boolean` | `true` | Show signup link |
166
+ | `showMagicLinkOption` | `boolean` | `false` | Show magic link option |
167
+ | `className` | `string` | โ€” | CSS class for the root element |
168
+
169
+ #### Copy (`LoginFormCopy`)
170
+
171
+ | Key | Default |
172
+ |-----|---------|
173
+ | `title` | `'Sign In'` |
174
+ | `usernameLabel` | `'Email or Phone'` |
175
+ | `usernamePlaceholder` | `'Enter your email or phone number'` |
176
+ | `passwordLabel` | `'Password'` |
177
+ | `passwordPlaceholder` | `'Enter your password'` |
178
+ | `submitButton` | `'Sign In'` |
179
+ | `loadingText` | `'Signing in...'` |
180
+ | `errorMessage` | `'Invalid credentials'` |
181
+ | `forgotPasswordLink` | `'Forgot your password?'` |
182
+ | `signupLink` | `'Sign up here'` |
183
+ | `signupText` | `"Don't have an account?"` |
184
+ | `magicLinkText` | `'Prefer passwordless?'` |
185
+ | `magicLinkLink` | `'Use Magic Link'` |
186
+ | `tenantNotFoundError` | `'Tenant not found'` |
187
+ | `dividerBullet` | `'โ€ข'` |
188
+ | `showPasswordAriaLabel` | `'Show password'` |
189
+ | `hidePasswordAriaLabel` | `'Hide password'` |
190
+
191
+ #### Styles (`LoginFormStyles`)
192
+
193
+ | Key | Targets |
194
+ |-----|---------|
195
+ | `container` | Root wrapper |
196
+ | `title` | `<h2>` heading |
197
+ | `form` | `<form>` element |
198
+ | `fieldGroup` | Each label+input group |
199
+ | `label` | `<label>` elements |
200
+ | `input` | `<input>` elements |
201
+ | `inputError` | Input in error state (merged on top of `input`) |
202
+ | `inputContainer` | Password field wrapper |
203
+ | `inputWithIcon` | Password input with toggle icon |
204
+ | `passwordToggle` | Show/hide password button |
205
+ | `button` | Submit button |
206
+ | `buttonDisabled` | Disabled state (merged on top of `button`) |
207
+ | `buttonLoading` | Loading state (merged on top of `button`) |
208
+ | `errorText` | Error message text |
209
+ | `linkContainer` | Links section wrapper |
210
+ | `link` | `<a>` link elements |
211
+ | `divider` | Bullet divider between links |
212
+
213
+ #### Usage Example
182
214
 
183
- // Login Form (supports email/phone + password)
215
+ ```tsx
184
216
  <LoginForm
185
- onSuccess={(user) => console.log('Logged in:', user)}
186
- onForgotPasswordClick={() => navigate('/forgot-password')}
187
- onSignupClick={() => navigate('/signup')}
217
+ copy={{
218
+ title: 'Bienvenido',
219
+ submitButton: 'Iniciar Sesion',
220
+ usernameLabel: 'Correo electronico',
221
+ forgotPasswordLink: 'ยฟOlvidaste tu contraseรฑa?',
222
+ }}
223
+ styles={{
224
+ container: { maxWidth: '500px', backgroundColor: '#f0f4f8' },
225
+ button: { backgroundColor: '#4f46e5', borderRadius: '12px' },
226
+ }}
227
+ icons={{
228
+ showPassword: <MyEyeIcon />,
229
+ hidePassword: <MyEyeOffIcon />,
230
+ }}
231
+ onSuccess={(data) => navigate('/dashboard')}
188
232
  onMagicLinkClick={() => navigate('/magic-link')}
189
- showMagicLinkOption={true}
233
+ showMagicLinkOption
190
234
  />
235
+ ```
191
236
 
192
- // Signup Form
193
- <SignupForm
194
- onSuccess={(user) => console.log('Signed up:', user)}
195
- onLoginClick={() => navigate('/login')}
196
- onMagicLinkClick={() => navigate('/magic-link')}
197
- showMagicLinkOption={true}
198
- />
237
+ ---
199
238
 
200
- // Magic Link Form
201
- <MagicLinkForm
202
- frontendUrl="https://yourapp.com"
203
- onSuccess={() => console.log('Magic link sent!')}
204
- onLoginClick={() => navigate('/login')}
205
- onSignupClick={() => navigate('/signup')}
206
- />
239
+ ### SignupForm
240
+
241
+ User registration form with email/phone, password, and optional tenant creation.
242
+
243
+ #### Props (`SignupFormProps`)
244
+
245
+ | Prop | Type | Default | Description |
246
+ |------|------|---------|-------------|
247
+ | `copy` | `SignupFormCopy` | See below | Override user-facing texts |
248
+ | `styles` | `SignupFormStyles` | See below | Override inline styles |
249
+ | `signupType` | `'user' \| 'tenant'` | `'user'` | User signup or tenant admin signup |
250
+ | `onSuccess` | `(data: any) => void` | โ€” | Called after successful signup |
251
+ | `onError` | `(error: string) => void` | โ€” | Called on signup failure |
252
+ | `onLoginClick` | `() => void` | โ€” | Navigate to login |
253
+ | `onMagicLinkClick` | `() => void` | โ€” | Navigate to magic link |
254
+ | `showLoginLink` | `boolean` | `true` | Show login link |
255
+ | `showMagicLinkOption` | `boolean` | `false` | Show magic link option |
256
+ | `className` | `string` | โ€” | CSS class for the root element |
257
+
258
+ #### Copy (`SignupFormCopy`)
259
+
260
+ | Key | Default |
261
+ |-----|---------|
262
+ | `title` | `'Create Account'` |
263
+ | `nameLabel` | `'First Name'` |
264
+ | `namePlaceholder` | `'Enter your first name'` |
265
+ | `lastNameLabel` | `'Last Name'` |
266
+ | `lastNamePlaceholder` | `'Enter your last name'` |
267
+ | `emailLabel` | `'Email'` |
268
+ | `emailPlaceholder` | `'Enter your email'` |
269
+ | `phoneNumberLabel` | `'Phone Number'` |
270
+ | `phoneNumberPlaceholder` | `'Enter your phone number'` |
271
+ | `passwordLabel` | `'Password'` |
272
+ | `passwordPlaceholder` | `'Enter your password'` |
273
+ | `confirmPasswordLabel` | `'Confirm Password'` |
274
+ | `confirmPasswordPlaceholder` | `'Confirm your password'` |
275
+ | `tenantNameLabel` | `'Organization Name'` |
276
+ | `tenantNamePlaceholder` | `'Enter your organization name'` |
277
+ | `submitButton` | `'Create Account'` |
278
+ | `loadingText` | `'Creating account...'` |
279
+ | `errorMessage` | `'Failed to create account'` |
280
+ | `passwordMismatchError` | `'Passwords do not match'` |
281
+ | `loginLink` | `'Sign in here'` |
282
+ | `loginText` | `'Already have an account?'` |
283
+ | `magicLinkText` | `'Prefer passwordless?'` |
284
+ | `magicLinkLink` | `'Use Magic Link'` |
285
+ | `isAdminLabel` | `'Create new organization'` |
286
+ | `isAdminDescription` | `'Check this if you want to create a new organization'` |
287
+ | `contactMethodHint` | `'At least one contact method (email or phone) is required'` |
288
+ | `tenantNotFoundError` | `'Tenant not found'` |
289
+ | `dividerBullet` | `'โ€ข'` |
290
+
291
+ #### Styles (`SignupFormStyles`)
292
+
293
+ | Key | Targets |
294
+ |-----|---------|
295
+ | `container` | Root wrapper |
296
+ | `title` | `<h2>` heading |
297
+ | `form` | `<form>` element |
298
+ | `fieldGroup` | Each label+input group |
299
+ | `label` | `<label>` elements |
300
+ | `input` | `<input>` elements |
301
+ | `inputError` | Input in error state |
302
+ | `checkbox` | Checkbox input |
303
+ | `checkboxContainer` | Checkbox + label wrapper |
304
+ | `checkboxLabel` | Checkbox label text |
305
+ | `button` | Submit button |
306
+ | `buttonDisabled` | Disabled state (merged on top of `button`) |
307
+ | `buttonLoading` | Loading state (merged on top of `button`) |
308
+ | `errorText` | Error message text |
309
+ | `linkContainer` | Links section wrapper |
310
+ | `link` | `<a>` link elements |
311
+ | `divider` | Bullet divider between links |
312
+ | `hintText` | Contact method hint text |
207
313
 
208
- // Magic Link Verification
209
- <MagicLinkVerify
210
- onSuccess={(data) => navigate('/dashboard')}
211
- onError={(error) => console.error(error)}
212
- onBackToLogin={() => navigate('/login')}
213
- />
314
+ ---
214
315
 
215
- // Password Recovery
216
- <PasswordRecoveryForm
217
- onSuccess={() => console.log('Recovery email sent!')}
218
- onBackToLogin={() => navigate('/login')}
219
- />
316
+ ### MagicLinkForm
317
+
318
+ Passwordless Magic Link send form. Handles both new and existing users.
319
+
320
+ #### Props (`MagicLinkFormProps`)
321
+
322
+ | Prop | Type | Default | Description |
323
+ |------|------|---------|-------------|
324
+ | `copy` | `MagicLinkFormCopy` | See below | Override user-facing texts |
325
+ | `styles` | `MagicLinkFormStyles` | See below | Override inline styles |
326
+ | `onSuccess` | `(data: any) => void` | โ€” | Called after magic link sent |
327
+ | `onError` | `(error: string) => void` | โ€” | Called on failure |
328
+ | `onLoginClick` | `() => void` | โ€” | Navigate to login |
329
+ | `onSignupClick` | `() => void` | โ€” | Navigate to signup |
330
+ | `showTraditionalLinks` | `boolean` | `true` | Show login/signup links |
331
+ | `className` | `string` | โ€” | CSS class for the root element |
332
+ | `verifyToken` | `string` | โ€” | Auto-verify a magic link token |
333
+ | `frontendUrl` | `string` | `window.location.origin` | Base URL for the magic link callback |
334
+
335
+ #### Copy (`MagicLinkFormCopy`)
336
+
337
+ | Key | Default |
338
+ |-----|---------|
339
+ | `title` | `'Sign In with Magic Link'` |
340
+ | `description` | `"Enter your email to receive a magic link..."` |
341
+ | `emailLabel` | `'Email'` |
342
+ | `emailPlaceholder` | `'Enter your email'` |
343
+ | `nameLabel` | `'Name'` |
344
+ | `namePlaceholder` | `'Enter your name'` |
345
+ | `lastNameLabel` | `'Last Name'` |
346
+ | `lastNamePlaceholder` | `'Enter your last name'` |
347
+ | `submitButton` | `'Send Magic Link'` |
348
+ | `loadingText` | `'Sending magic link...'` |
349
+ | `successMessage` | `'Magic link sent! Check your email...'` |
350
+ | `errorMessage` | `'Failed to send magic link. Please try again.'` |
351
+ | `verifyingText` | `'Verifying magic link...'` |
352
+ | `verifyingDescription` | `'Please wait while we verify your magic link...'` |
353
+ | `showNameToggle` | `'New user? Add your name'` |
354
+ | `hideNameToggle` | `'Existing user? Hide name fields'` |
355
+ | `loginLink` | `'Sign in with password'` |
356
+ | `loginText` | `'Already have an account?'` |
357
+ | `signupLink` | `'Sign up with password'` |
358
+ | `signupText` | `'Prefer traditional signup?'` |
359
+ | `tenantNotFoundError` | `'Tenant not found'` |
360
+ | `missingTenantOrEmailError` | `'Missing tenant or email'` |
361
+ | `dividerBullet` | `'โ€ข'` |
362
+
363
+ #### Styles (`MagicLinkFormStyles`)
364
+
365
+ | Key | Targets |
366
+ |-----|---------|
367
+ | `container` | Root wrapper |
368
+ | `title` | `<h2>` heading |
369
+ | `description` | Description paragraph |
370
+ | `form` | `<form>` element |
371
+ | `fieldGroup` | Each label+input group |
372
+ | `label` | `<label>` elements |
373
+ | `input` | `<input>` elements |
374
+ | `inputError` | Input in error state |
375
+ | `button` | Submit button |
376
+ | `buttonDisabled` | Disabled state (merged on top of `button`) |
377
+ | `buttonLoading` | Loading state (merged on top of `button`) |
378
+ | `errorText` | Error message text |
379
+ | `successText` | Success message text |
380
+ | `linkContainer` | Links section wrapper |
381
+ | `link` | `<a>` link elements |
382
+ | `divider` | Bullet divider between links |
383
+ | `verifyingContainer` | Verification loading wrapper |
384
+ | `verifyingText` | Verification description text |
385
+ | `toggleContainer` | Name fields toggle wrapper |
386
+ | `toggleLink` | "New user? Add your name" toggle button |
387
+
388
+ ---
389
+
390
+ ### MagicLinkVerify
391
+
392
+ Automatic Magic Link verification component. Reads token from URL params or accepts them as props.
393
+
394
+ #### Props (`MagicLinkVerifyProps`)
395
+
396
+ | Prop | Type | Default | Description |
397
+ |------|------|---------|-------------|
398
+ | `copy` | `MagicLinkVerifyCopy` | See below | Override user-facing texts |
399
+ | `styles` | `MagicLinkVerifyStyles` | See below | Override inline styles |
400
+ | `icons` | `MagicLinkVerifyIcons` | SVG icons | Override loading/success/error icons |
401
+ | `onSuccess` | `(data: any) => void` | โ€” | Called after successful verification |
402
+ | `onError` | `(error: string) => void` | โ€” | Called on verification failure |
403
+ | `onRetry` | `() => void` | โ€” | Called before retry attempt |
404
+ | `onBackToLogin` | `() => void` | โ€” | Navigate back to login |
405
+ | `className` | `string` | โ€” | CSS class for the root element |
406
+ | `token` | `string` | URL param | Magic link token (auto-extracted from `?token=`) |
407
+ | `email` | `string` | URL param | User email (auto-extracted from `?email=`) |
408
+ | `appId` | `string` | URL param | App ID (auto-extracted from `?appId=`) |
409
+ | `tenantSlug` | `string` | URL param | Tenant slug (auto-extracted from `?tenantSlug=`) |
410
+ | `autoRedirectDelay` | `number` | `3000` | Milliseconds before auto-redirect (0 to disable) |
411
+
412
+ #### Copy (`MagicLinkVerifyCopy`)
413
+
414
+ | Key | Default |
415
+ |-----|---------|
416
+ | `title` | `'Verifying Magic Link'` |
417
+ | `verifyingMessage` | `'Please wait while we verify your magic link...'` |
418
+ | `successMessage` | `'Magic link verified successfully! You are now logged in.'` |
419
+ | `errorMessage` | `'Failed to verify magic link. The link may be expired or invalid.'` |
420
+ | `redirectingMessage` | `'Redirecting you to the dashboard...'` |
421
+ | `retryButton` | `'Try Again'` |
422
+ | `backToLoginButton` | `'Back to Login'` |
423
+ | `missingParamsError` | `'Missing required parameters: token or email'` |
424
+
425
+ #### Styles (`MagicLinkVerifyStyles`)
426
+
427
+ | Key | Targets |
428
+ |-----|---------|
429
+ | `container` | Root wrapper |
430
+ | `card` | Inner card (kept for compatibility) |
431
+ | `title` | `<h1>` heading |
432
+ | `message` | Verifying/redirecting message |
433
+ | `successMessage` | Success state message |
434
+ | `errorMessage` | Error state message |
435
+ | `spinner` | Loading spinner |
436
+ | `buttonContainer` | Error buttons wrapper |
437
+ | `retryButton` | "Try Again" button |
438
+ | `retryButtonHover` | Hover state for retry button |
439
+ | `backButton` | "Back to Login" button |
440
+ | `backButtonHover` | Hover state for back button |
441
+
442
+ #### Icons (`MagicLinkVerifyIcons`)
443
+
444
+ | Key | Default | Description |
445
+ |-----|---------|-------------|
446
+ | `loading` | Animated spinner | Shown during verification |
447
+ | `success` | Green checkmark SVG | Shown on success |
448
+ | `error` | Red X circle SVG | Shown on error |
449
+
450
+ ---
451
+
452
+ ### PasswordRecoveryForm
453
+
454
+ Password reset flow with two modes: request (send email) and reset (set new password).
455
+
456
+ #### Props (`PasswordRecoveryFormProps`)
457
+
458
+ | Prop | Type | Default | Description |
459
+ |------|------|---------|-------------|
460
+ | `copy` | `PasswordRecoveryFormCopy` | See below | Override user-facing texts |
461
+ | `styles` | `PasswordRecoveryFormStyles` | See below | Override inline styles |
462
+ | `mode` | `'request' \| 'reset'` | `'request'` | Current form mode |
463
+ | `token` | `string` | โ€” | Pre-fill reset token |
464
+ | `onSuccess` | `(data?: any) => void` | โ€” | Called after success |
465
+ | `onError` | `(error: string) => void` | โ€” | Called on failure |
466
+ | `onBackToLogin` | `() => void` | โ€” | Navigate back to login |
467
+ | `onModeChange` | `(mode: 'request' \| 'reset') => void` | โ€” | Show mode switch links |
468
+ | `className` | `string` | โ€” | CSS class for the root element |
469
+
470
+ #### Copy (`PasswordRecoveryFormCopy`)
471
+
472
+ | Key | Default |
473
+ |-----|---------|
474
+ | `title` | `'Reset Password'` |
475
+ | `subtitle` | `"Enter your email address and we'll send you a link..."` |
476
+ | `emailLabel` | `'Email'` |
477
+ | `emailPlaceholder` | `'Enter your email'` |
478
+ | `submitButton` | `'Send Reset Link'` |
479
+ | `loadingText` | `'Sending...'` |
480
+ | `successMessage` | `'Password reset link sent! Check your email.'` |
481
+ | `errorMessage` | `'Failed to send reset link'` |
482
+ | `backToLoginLink` | `'Back to Sign In'` |
483
+ | `resetTitle` | `'Set New Password'` |
484
+ | `resetSubtitle` | `'Enter your reset token and new password.'` |
485
+ | `tokenLabel` | `'Reset Token'` |
486
+ | `tokenPlaceholder` | `'Enter reset token from email'` |
487
+ | `newPasswordLabel` | `'New Password'` |
488
+ | `newPasswordPlaceholder` | `'Enter new password'` |
489
+ | `confirmPasswordLabel` | `'Confirm Password'` |
490
+ | `confirmPasswordPlaceholder` | `'Confirm new password'` |
491
+ | `resetSubmitButton` | `'Reset Password'` |
492
+ | `resetLoadingText` | `'Resetting...'` |
493
+ | `resetSuccessMessage` | `'Password reset successfully!'` |
494
+ | `passwordMismatchError` | `'Passwords do not match'` |
495
+ | `requestNewLinkLink` | `'Request New Link'` |
496
+ | `haveTokenLink` | `'I have a token'` |
497
+ | `tenantNotFoundError` | `'Tenant not found'` |
498
+ | `dividerBullet` | `'โ€ข'` |
499
+
500
+ #### Styles (`PasswordRecoveryFormStyles`)
501
+
502
+ | Key | Targets |
503
+ |-----|---------|
504
+ | `container` | Root wrapper |
505
+ | `title` | `<h2>` heading |
506
+ | `subtitle` | Subtitle paragraph |
507
+ | `form` | `<form>` element |
508
+ | `fieldGroup` | Each label+input group |
509
+ | `label` | `<label>` elements |
510
+ | `input` | `<input>` elements |
511
+ | `inputError` | Input in error state |
512
+ | `button` | Submit button |
513
+ | `buttonDisabled` | Disabled state (merged on top of `button`) |
514
+ | `buttonLoading` | Loading state (merged on top of `button`) |
515
+ | `errorText` | Error message text |
516
+ | `successText` | Success message text |
517
+ | `linkContainer` | Links section wrapper |
518
+ | `link` | `<a>` link elements |
519
+ | `modeSwitchDivider` | Bullet divider between mode links |
520
+
521
+ ---
522
+
523
+ ### TenantSelector
524
+
525
+ Dropdown component for switching between tenants. Integrates with `AuthProvider` context automatically.
526
+
527
+ #### Props (`TenantSelectorProps`)
528
+
529
+ | Prop | Type | Default | Description |
530
+ |------|------|---------|-------------|
531
+ | `tenants` | `UserTenantMembership[]` | From context | Override tenant list |
532
+ | `currentTenantId` | `string \| null` | From context | Override current tenant |
533
+ | `onSelect` | `(tenantId: string) => void` | `auth.switchToTenant` | Custom selection handler |
534
+ | `styles` | `TenantSelectorStyles` | See below | Override inline styles |
535
+ | `className` | `string` | โ€” | CSS class for root element |
536
+ | `dropdownClassName` | `string` | โ€” | CSS class for dropdown |
537
+ | `itemClassName` | `string` | โ€” | CSS class for each item |
538
+ | `renderItem` | `(tenant, isSelected) => ReactNode` | Default renderer | Custom item renderer |
539
+ | `placeholder` | `string` | `'Select tenant'` | Placeholder when no tenant selected |
540
+ | `disabled` | `boolean` | `false` | Disable the selector |
541
+ | `showCurrentTenant` | `boolean` | `true` | Show name when only 1 tenant |
542
+
543
+ #### Styles (`TenantSelectorStyles`)
544
+
545
+ | Key | Targets |
546
+ |-----|---------|
547
+ | `wrapper` | Root `<div>` (position: relative) |
548
+ | `button` | Trigger button |
549
+ | `buttonDisabled` | Disabled button state (merged on top of `button`) |
550
+ | `dropdown` | Dropdown menu container |
551
+ | `item` | Each tenant item |
552
+ | `itemSelected` | Selected tenant item (merged on top of `item`) |
553
+ | `itemHover` | Hover state for items |
554
+ | `itemRole` | Role badge next to tenant name |
555
+ | `arrow` | Arrow indicator (โ–ฒ/โ–ผ) |
556
+
557
+ ---
558
+
559
+ ### Protected
560
+
561
+ Conditionally renders content based on permissions and/or roles.
562
+
563
+ ```tsx
564
+ <Protected
565
+ requiredPermissions={['users:read', 'users:write']}
566
+ requireAll={true}
567
+ fallback={<div>Access denied</div>}
568
+ >
569
+ <AdminPanel />
570
+ </Protected>
220
571
  ```
221
572
 
222
- ### Customization
573
+ | Prop | Type | Default | Description |
574
+ |------|------|---------|-------------|
575
+ | `requiredPermissions` | `string[]` | โ€” | Required permissions |
576
+ | `requiredRole` | `string` | โ€” | Required user role |
577
+ | `requireAll` | `boolean` | `true` | All permissions required? |
578
+ | `fallback` | `ReactNode` | `null` | Shown when access denied |
579
+ | `onUnauthorized` | `() => void` | โ€” | Callback on denial |
580
+
581
+ ---
582
+
583
+ ## ๐ŸŽจ Customization Examples
584
+
585
+ ### Internationalization (i18n)
223
586
 
224
- All components support full customization of copy, styles, and icons:
587
+ Override all user-facing text for your locale:
225
588
 
226
589
  ```tsx
227
590
  <LoginForm
228
591
  copy={{
229
- title: 'Welcome Back',
230
- submitButton: 'Sign In',
231
- usernameLabel: 'Email or Phone',
232
- }}
233
- styles={{
234
- container: { backgroundColor: '#f8f9fa' },
235
- button: { backgroundColor: '#007bff' },
592
+ title: 'Iniciar Sesion',
593
+ usernameLabel: 'Correo o Telefono',
594
+ usernamePlaceholder: 'Ingrese su correo o telefono',
595
+ passwordLabel: 'Contraseรฑa',
596
+ passwordPlaceholder: 'Ingrese su contraseรฑa',
597
+ submitButton: 'Entrar',
598
+ loadingText: 'Ingresando...',
599
+ errorMessage: 'Credenciales invalidas',
600
+ forgotPasswordLink: 'ยฟOlvidaste tu contraseรฑa?',
601
+ signupText: 'ยฟNo tienes cuenta?',
602
+ signupLink: 'Registrate aqui',
603
+ showPasswordAriaLabel: 'Mostrar contraseรฑa',
604
+ hidePasswordAriaLabel: 'Ocultar contraseรฑa',
236
605
  }}
606
+ />
607
+ ```
608
+
609
+ ### Brand Theming
610
+
611
+ Apply your brand colors and spacing:
612
+
613
+ ```tsx
614
+ const brandStyles = {
615
+ container: {
616
+ maxWidth: '480px',
617
+ backgroundColor: '#1a1a2e',
618
+ borderRadius: '16px',
619
+ padding: '3rem',
620
+ },
621
+ title: { color: '#e94560', fontSize: '2rem' },
622
+ input: {
623
+ backgroundColor: '#16213e',
624
+ color: '#ffffff',
625
+ border: '1px solid #0f3460',
626
+ borderRadius: '8px',
627
+ },
628
+ button: {
629
+ backgroundColor: '#e94560',
630
+ borderRadius: '8px',
631
+ fontWeight: '600',
632
+ },
633
+ link: { color: '#e94560' },
634
+ };
635
+
636
+ <LoginForm styles={brandStyles} />
637
+ <SignupForm styles={brandStyles} />
638
+ <MagicLinkForm styles={brandStyles} />
639
+ ```
640
+
641
+ ### Custom Icons
642
+
643
+ ```tsx
644
+ import { Eye, EyeOff } from 'lucide-react';
645
+
646
+ <LoginForm
237
647
  icons={{
238
- showPassword: <CustomEyeIcon />,
239
- hidePassword: <CustomEyeOffIcon />,
648
+ showPassword: <Eye size={16} />,
649
+ hidePassword: <EyeOff size={16} />,
240
650
  }}
241
651
  />
242
652
  ```
243
653
 
654
+ ---
655
+
244
656
  ## ๐Ÿ—๏ธ Architecture
245
657
 
246
658
  ### Core Providers
247
659
 
248
- - **AppProvider** - Application configuration and context
249
- - **TenantProvider** - Multi-tenant configuration and management
250
- - **AuthProvider** - Authentication and session management
251
- - **FeatureFlagProvider** - Feature flag management
252
- - **SubscriptionProvider** - Billing and subscription handling
660
+ | Provider | Purpose |
661
+ |----------|---------|
662
+ | **AppProvider** | Application configuration (baseUrl, appId) |
663
+ | **TenantProvider** | Multi-tenant detection and management |
664
+ | **AuthProvider** | Authentication, session, and user data |
665
+ | **FeatureFlagProvider** | Feature flag management |
666
+ | **SubscriptionProvider** | Billing and subscription handling |
667
+ | **RoutingProvider** | Zone-based routing (RFC-005) |
253
668
 
254
669
  ### Permission System
255
670
 
256
- The library uses a resource-action permission format:
671
+ The library uses a `resource:action` permission format:
257
672
 
258
673
  ```
259
- resource:action
674
+ users:read - Read user data
675
+ products:write - Create/update products
676
+ admin:* - All admin permissions
677
+ reports:read - View reports
260
678
  ```
261
679
 
262
- Examples:
263
- - `users:read` - Read user data
264
- - `products:write` - Create/update products
265
- - `admin:*` - All admin permissions
266
- - `reports:read` - View reports
267
-
268
680
  ## ๐Ÿ“š Documentation
269
681
 
270
682
  - [๐Ÿ“– Implementation Guide](./docs/implementation.md)
271
683
  - [๐Ÿ”ง Advanced Usage](./docs/advanced-usage.md)
272
- - [๐Ÿค Contributing](./docs/contributing.md)
273
684
  - [๐Ÿ“‹ API Reference](./docs/api-reference.md)
274
685
  - [๐ŸŽฏ Examples](./docs/examples.md)
686
+ - [โœจ Magic Link Guide](./MAGIC_LINK_USAGE.md)
687
+ - [๐Ÿ›ฃ๏ธ Zone Routing](./docs/ZONE_ROUTING.md)
688
+ - [๐Ÿค Contributing](./docs/contributing.md)
275
689
 
276
690
  ## ๐ŸŽฎ Demo Application
277
691
 
278
- A complete demo application is included in the `example/` directory. To run it:
692
+ A complete demo application is included in the `example/` directory:
279
693
 
280
694
  ```bash
281
695
  cd example
@@ -299,7 +713,7 @@ The demo showcases:
299
713
  ### Prerequisites
300
714
 
301
715
  - Node.js 18+
302
- - yarn (recommended) or npm
716
+ - yarn
303
717
 
304
718
  ### Setup
305
719
 
@@ -317,26 +731,26 @@ yarn build
317
731
  # Run tests
318
732
  yarn test
319
733
 
320
- # Run CI pipeline
734
+ # Run full CI pipeline (type-check + test + build)
321
735
  yarn ci
322
-
323
- # Start example app
324
- cd example && yarn start
325
736
  ```
326
737
 
327
738
  ### Project Structure
328
739
 
329
740
  ```
330
741
  react-identity-access/
331
- โ”œโ”€โ”€ src/ # Library source code
332
- โ”‚ โ”œโ”€โ”€ components/ # React components (forms, guards, etc.)
333
- โ”‚ โ”œโ”€โ”€ providers/ # Context providers (Auth, Tenant, etc.)
742
+ โ”œโ”€โ”€ src/
743
+ โ”‚ โ”œโ”€โ”€ components/ # React components (forms, guards, selectors)
744
+ โ”‚ โ”œโ”€โ”€ providers/ # Context providers (App, Tenant, Auth, etc.)
334
745
  โ”‚ โ”œโ”€โ”€ services/ # API services and HTTP client
335
- โ”‚ โ”œโ”€โ”€ types/ # TypeScript definitions
336
- โ”‚ โ””โ”€โ”€ index.ts # Main export
746
+ โ”‚ โ”œโ”€โ”€ hooks/ # Custom React hooks
747
+ โ”‚ โ”œโ”€โ”€ types/ # TypeScript type definitions
748
+ โ”‚ โ”œโ”€โ”€ utils/ # Utility functions
749
+ โ”‚ โ”œโ”€โ”€ errors/ # Custom error classes
750
+ โ”‚ โ””โ”€โ”€ index.ts # Public API exports
337
751
  โ”œโ”€โ”€ example/ # Demo application
338
752
  โ”œโ”€โ”€ docs/ # Documentation
339
- โ”œโ”€โ”€ dist/ # Built library
753
+ โ”œโ”€โ”€ dist/ # Built library (ES + CJS)
340
754
  โ””โ”€โ”€ package.json
341
755
  ```
342
756
 
@@ -356,24 +770,20 @@ REACT_APP_TENANT_MODE=subdomain
356
770
  // AppProvider Config
357
771
  interface AppConfig {
358
772
  baseUrl: string; // API base URL
359
- appId: string; // Application identifier
360
- apiTimeout?: number; // Request timeout (default: 30000)
361
- retryAttempts?: number; // Retry attempts (default: 3)
773
+ appId: string; // Application identifier
362
774
  }
363
775
 
364
776
  // TenantProvider Config
365
777
  interface TenantConfig {
366
- tenantMode: 'subdomain' | 'selector' | 'fixed';
367
- selectorParam?: string; // For 'selector' mode
368
- fixedTenantSlug?: string; // For 'fixed' mode
369
- initialTenant?: string; // Initial tenant value
778
+ tenantMode: 'subdomain' | 'selector' | 'fixed' | 'optional';
779
+ selectorParam?: string; // For 'selector' mode
780
+ fixedTenantSlug?: string; // For 'fixed' mode
781
+ initialTenant?: string; // Initial tenant value
370
782
  }
371
783
  ```
372
784
 
373
785
  ## ๐Ÿงช Testing
374
786
 
375
- The library includes comprehensive tests:
376
-
377
787
  ```bash
378
788
  # Run all tests
379
789
  yarn test
@@ -394,11 +804,11 @@ yarn test:coverage
394
804
 
395
805
  ## ๐Ÿ”’ Security
396
806
 
397
- - **JWT tokens** with automatic refresh
398
- - **Secure storage** of sensitive data
399
- - **CSRF protection** built-in
807
+ - **JWT tokens** with automatic refresh and proactive renewal
808
+ - **Secure token storage** with configurable backends
809
+ - **Session generation tracking** to prevent stale token usage
400
810
  - **Permission validation** on both client and server
401
- - **Audit logging** for security events
811
+ - **Console output suppressed** in production and test environments
402
812
 
403
813
  ## ๐ŸŒ Browser Support
404
814
 
@@ -424,14 +834,14 @@ We welcome contributions! Please see our [Contributing Guide](./docs/contributin
424
834
 
425
835
  ## ๐ŸŽฏ Roadmap
426
836
 
427
- - [x] **Magic Link Authentication** - Passwordless authentication via email โœ…
428
- - [x] **Email/Phone Login Support** - Flexible authentication methods โœ…
429
- - [x] **Pre-built Form Components** - Ready-to-use authentication forms โœ…
430
- - [x] **Multi-tenant Architecture** - Separate App and Tenant providers โœ…
837
+ - [x] **Magic Link Authentication** - Passwordless authentication via email
838
+ - [x] **Email/Phone Login Support** - Flexible authentication methods
839
+ - [x] **Pre-built Form Components** - Ready-to-use, fully customizable authentication forms
840
+ - [x] **Multi-tenant Architecture** - Separate App and Tenant providers
841
+ - [x] **Zone-based Routing** - Declarative route access control (RFC-005)
842
+ - [x] **Customizable Copy & Styles** - All components support i18n and brand theming
431
843
  - [ ] **OAuth 2.0 / OpenID Connect** - Social login integration
432
844
  - [ ] **Multi-factor Authentication** - SMS/TOTP support
433
- - [ ] **Advanced Audit Logging** - Comprehensive security tracking
434
- - [ ] **GraphQL Integration** - GraphQL API support
435
845
  - [ ] **React Native Support** - Mobile app integration
436
846
  - [ ] **SSR/Next.js Optimization** - Server-side rendering support
437
847
  - [ ] **Biometric Authentication** - WebAuthn/FIDO2 support