Overview
MotorDesk uses a Redux-based authentication system with JWT tokens for session management. The system supports both login for existing users and registration for new business owners.
Authentication state is persisted using Redux Persist, allowing users to remain logged in even after closing the browser.
Authentication Architecture
The authentication system consists of three main components:
useAuth Hook (src/hooks/useAuth.ts): Custom React hook that provides authentication methods
Auth Slice (src/store/slices/authSlice.ts): Redux store slice managing authentication state
App Router (src/routes/AppRouter.tsx): Route protection and navigation logic
State Structure
The authentication state in Redux includes:
src/store/slices/authSlice.ts
interface AuthState {
user : User | null ; // Current authenticated user
token : string | null ; // JWT authentication token
isAuthenticated : boolean ; // Authentication status
isLoading : boolean ; // Loading state for async operations
error : string | null ; // Error messages
needsOnboarding : boolean ; // Flag for new users requiring setup
}
User Model
Each user in the system has the following structure:
src/store/slices/authSlice.ts
export interface User {
id : string ; // Unique user identifier
branchIds : string []; // Branch locations user has access to
nombre : string ; // User's full name
email : string ; // Email (used for login)
rol : UserRole ; // User role (OWNER, ADMIN, SELLER, CASHIER)
}
Login Flow
Using the Login Hook
The useAuth hook provides a complete authentication interface:
import { useAuth } from '@hooks/useAuth' ;
export const LoginComponent = () => {
const {
email ,
setEmail ,
password ,
setPassword ,
handleLogin ,
isLoading ,
error ,
isAuthenticated
} = useAuth ();
const onSubmit = async ( e : React . FormEvent ) => {
e . preventDefault ();
const success = await handleLogin ( e );
if ( success ) {
// Redirect to dashboard
navigate ( '/dashboard' );
}
};
return (
< form onSubmit = { onSubmit } >
< input
type = "email"
value = { email }
onChange = {(e) => setEmail (e.target.value)}
/>
< input
type = "password"
value = { password }
onChange = {(e) => setPassword (e.target.value)}
/>
< button type = "submit" disabled = { isLoading } >
{ isLoading ? 'Logging in...' : 'Login' }
</ button >
{ error && < p >{ error }</ p >}
</ form >
);
};
Login Process
The login function performs the following steps:
Dispatch login start
Sets isLoading to true and clears any previous errors: dispatch ( loginStart ()); // Activates loading state
Validate credentials
Checks credentials against the user database: // Simulates server latency (1500ms)
await new Promise ( resolve => setTimeout ( resolve , 1500 ));
// Find user by email
const userFound = usersData . users . find (
u => u . email === email . toLowerCase ()
);
// Validate password (currently accepts '123456' for demo)
if ( userFound && password === '123456' ) {
// Success path
} else {
dispatch ( loginFailure ( 'Credenciales incorrectas' ));
return false ;
}
Dispatch login success
On successful authentication, updates the Redux store: dispatch ( loginSuccess ({
user: userFound as User ,
isNew: false ,
token: `mock-jwt-token-para- ${ userFound . id } `
}));
This sets:
isAuthenticated to true
Stores user data and token
Clears loading and error states
Handle errors
If authentication fails, error state is updated: dispatch ( loginFailure (
'Credenciales incorrectas. Verifica tu correo o contraseña.'
));
For development/demo purposes, any registered email with password 123456 will successfully authenticate.
Registration Flow
New business owners can register through the registration form.
Registration Process
const { register , isLoading , error } = useAuth ();
const handleRegister = async () => {
const success = await register (
'Juan Pérez' , // nombre
'[email protected] ' , // email
'123456' // password (optional)
);
if ( success ) {
// User is automatically logged in
// needsOnboarding will be true
navigate ( '/onboarding' );
}
};
Check for existing user
Validates that the email isn’t already registered: const userExists = usersData . users . find (
( u ) => u . email === emailNuevo . toLowerCase ()
);
if ( userExists ) {
dispatch ( loginFailure (
'Este correo ya está registrado en el sistema.'
));
return false ;
}
Create new user
Creates a new user with OWNER role: const newUser : User = {
id: `user-new- ${ Date . now () } ` ,
branchIds: [], // Empty initially
nombre: nombreNuevo ,
email: emailNuevo . toLowerCase (),
rol: UserRole . OWNER , // Auto-assigned OWNER role
};
Auto-login with onboarding flag
Automatically logs in the new user with onboarding enabled: dispatch ( loginSuccess ({
user: newUser ,
isNew: true , // Triggers onboarding
token: `mock-jwt-token-new- ${ newUser . id } `
}));
New registrations always receive the OWNER role, which grants full system access. Additional users with different roles must be created by the owner through the Settings panel.
Protected Routes
MotorDesk uses route guards to protect authenticated pages:
export const AppRouter = () => {
const isAuthenticated = useSelector (
( state : RootState ) => state . auth . isAuthenticated
);
return (
< BrowserRouter >
< Routes >
{ /* Public route - redirects if logged in */ }
< Route
path = "/login"
element = {
! isAuthenticated ? < Login /> : < Navigate to = "/dashboard" replace />
}
/>
{ /* Protected routes - requires authentication */ }
< Route
element = {
isAuthenticated ? < MainLayout /> : < Navigate to = "/login" replace />
}
>
< Route path = "/dashboard" element = { < Home /> } />
< Route path = "/vehicles" element = { < Vehicles /> } />
< Route path = "/sales" element = { < Sales /> } />
{ /* ... more protected routes */ }
</ Route >
</ Routes >
</ BrowserRouter >
);
};
Route Protection Logic
Unauthenticated Users
Can only access /login
Redirected to /login when accessing protected routes
Cannot access dashboard or app features
Authenticated Users
Cannot access /login (redirected to /dashboard)
Full access to all protected routes
Unknown routes redirect to /dashboard
Session Management
Logout Functionality
Users can logout, which clears all authentication state:
const { logout } = useAuth ();
const handleLogout = () => {
logout ();
// User is redirected to /login automatically
};
The logout action resets the auth state:
src/store/slices/authSlice.ts
logout : ( state ) => {
state . user = null ;
state . token = null ;
state . isAuthenticated = false ;
state . needsOnboarding = false ;
}
Onboarding Completion
For new users who need to complete onboarding:
Example: Complete Onboarding
const { needsOnboarding , finishOnboarding } = useAuth ();
if ( needsOnboarding ) {
// Show onboarding wizard
// After completion:
finishOnboarding ();
}
src/store/slices/authSlice.ts
completeOnboarding : ( state ) => {
state . needsOnboarding = false ;
}
State Persistence
Authentication state is persisted using Redux Persist with LocalForage:
import { persistStore , persistReducer } from 'redux-persist' ;
import localforage from 'localforage' ;
const persistConfig = {
key: 'root' ,
storage: localforage , // Uses IndexedDB for larger storage
whitelist: [ 'auth' ] // Only persist auth state
};
Persisted data allows users to remain logged in across browser sessions and enables full offline functionality.
Testing Credentials
For development and testing, use these mock credentials:
Email Password Role Branch Access [email protected] 123456ADMINISTRADOR branch-001, branch-002 [email protected] 123456VENDEDOR branch-001 [email protected] 123456VENDEDOR branch-002 [email protected] 123456CAJERO branch-003
All test users accept password 123456 for demo purposes. In production, implement proper password validation and hashing.
Security Considerations
The current implementation uses mock authentication for development. For production deployment, you must:
Implement secure password hashing (bcrypt, argon2)
Use real JWT tokens with proper signing
Add token refresh mechanism
Implement rate limiting on login attempts
Add multi-factor authentication (MFA)
Use HTTPS for all API calls
Implement CSRF protection
Next Steps
User Roles Learn about the different user roles and their permissions
State Management Understand how Redux manages application state
useAuth Hook Full API reference for the useAuth hook
Settings Configure user accounts and team management