Overview
TradeMaster Transactions implements a robust authentication system using Firebase Authentication as the primary provider, with Auth0 available as an alternative option. The system includes user validation, role-based access control, and persistent session management.
Authentication Architecture
Primary Authentication Flow
Firebase Authentication
Firebase Configuration
Firebase is initialized with project-specific credentials:
import firebase from 'firebase/compat/app' ;
import 'firebase/compat/auth' ;
import 'firebase/compat/firestore' ;
import { getFunctions , httpsCallable } from 'firebase/functions' ;
const firebaseConfig = {
apiKey: process . env . FIREBASE_API_KEY ,
authDomain: process . env . FIREBASE_AUTH_DOMAIN ,
projectId: process . env . FIREBASE_PROJECT_ID ,
storageBucket: process . env . FIREBASE_STORAGE_BUCKET ,
messagingSenderId: process . env . FIREBASE_MESSAGING_SENDER_ID ,
appId: process . env . FIREBASE_APP_ID
};
const app = firebase . initializeApp ( firebaseConfig );
const Auth = firebase . auth ();
const Firestore = firebase . firestore ();
const functions = getFunctions ( app );
export { Auth , Firestore , firebase };
The actual configuration values are stored securely and not committed to version control.
Authentication Context Provider
The AuthProvider manages authentication state and provides auth methods throughout the application:
import { createContext , useEffect } from 'react' ;
import { firebase , Firestore } from './Firebase' ;
import { useDispatch } from 'react-redux' ;
import { authStateChanged } from '../../store/apps/auth/authSlice' ;
const AuthContext = createContext ({
platform: 'Firebase' ,
signup : () => Promise . resolve (),
signin : () => Promise . resolve (),
logout : () => Promise . resolve (),
resetPassword : () => Promise . resolve (),
});
export const AuthProvider = ({ children }) => {
const dispatch = useDispatch ();
useEffect (() => {
const unsubscribe = firebase . auth (). onAuthStateChanged ( async ( user ) => {
if ( user ) {
// Fetch user profile from Firestore
const querySnapshot = await Firestore . collection ( 'u_clients' )
. doc ( user . uid ). get ();
// Validate user status
if ( querySnapshot . data ()?. status !== true ) {
logout ();
return ;
}
// Track user's last access and IP
const queryIP = await fetch ( 'https://ipapi.co/json/' );
const data = await queryIP . json ();
await Firestore . collection ( 'u_clients' ). doc ( user . uid ). update ({
"date.last_access" : firebase . firestore . FieldValue . serverTimestamp (),
last_IP: data . ip
});
// Update Redux state
dispatch ( authStateChanged ({
isAuthenticated: true ,
user: {
uid: user . uid ,
email: user . email ,
avatar: querySnapshot . data ()?. image ?? null ,
account_type: querySnapshot . data ()?. account_type ,
name: querySnapshot . data ()?. name ,
... querySnapshot . data ()
},
}));
} else {
dispatch ( authStateChanged ({
isAuthenticated: false ,
user: null ,
}));
}
});
return () => unsubscribe ();
}, [ dispatch ]);
return (
< AuthContext.Provider value = { { ... } } >
{ children }
</ AuthContext.Provider >
);
};
Authentication Methods
Email/Password Sign In
const signin = ( email , password ) =>
firebase . auth (). signInWithEmailAndPassword ( email , password );
Email/Password Sign Up
const signup = ( email , password ) =>
firebase . auth (). createUserWithEmailAndPassword ( email , password );
Social Authentication
const loginWithGoogle = () => {
const provider = new firebase . auth . GoogleAuthProvider ();
return firebase . auth (). signInWithPopup ( provider );
};
const loginWithFaceBook = () => {
const provider = new firebase . auth . FacebookAuthProvider ();
return firebase . auth (). signInWithPopup ( provider );
};
Password Reset
const ResetPassword = ( email ) =>
firebase . auth (). sendPasswordResetEmail ( email );
Sign Out
const logout = () => firebase . auth (). signOut ();
Login Implementation
The login form implements validation and platform-specific user verification:
import { useFormik } from 'formik' ;
import * as Yup from 'yup' ;
import useAuth from 'src/guards/authGuard/UseAuth' ;
import { validateUserPlatform } from '../../../guards/firebase/Firebase' ;
import { toast } from 'react-toastify' ;
const AuthLogin = () => {
const { signin } = useAuth ();
const LoginSchema = Yup . object (). shape ({
email: Yup . string ()
. email ( 'El correo electrónico es invalido' )
. required ( 'Correo electrónico es requerido' ),
password: Yup . string ()
. min ( 6 , 'La contraseña debe tener al menos 6 caracteres' )
. required ( 'Se requiere contraseña' ),
});
const formik = useFormik ({
initialValues: {
email: '' ,
password: '' ,
},
validationSchema: LoginSchema ,
onSubmit : async ( values , { setErrors , setStatus , setSubmitting }) => {
try {
// Validate user has access to the platform
const resp = await validateUserPlatform ({
platform: 'u_clients' ,
email: values . email
});
if ( resp . data . valido === true ) {
await signin ( values . email , values . password );
setStatus ({ success: true });
} else {
toast . error ( 'Usuario no autorizado.' );
}
} catch ( err ) {
const errorMessage = getErrorMessage ( err . code );
toast . error ( errorMessage );
setSubmitting ( false );
}
},
});
const getErrorMessage = ( errorCode ) => {
switch ( errorCode ) {
case "auth/invalid-email" :
return "La dirección de correo electrónico no es válida." ;
case "auth/invalid-credential" :
return "La contraseña o el correo es incorrecto." ;
case "auth/too-many-requests" :
return "Has echo muchas solicitudes, por favor espera." ;
default :
return "Ocurrió un error al intentar iniciar sesión." ;
}
};
};
The platform uses a Cloud Function validateUserPlatform to verify that users exist in the correct Firestore collection before allowing authentication.
Auth0 Integration (Alternative)
Auth0 is available as an alternative authentication provider:
import { createContext , useEffect , useReducer } from 'react' ;
import { Auth0Client } from '@auth0/auth0-spa-js' ;
const auth0Config = {
client_id: process . env . AUTH0_CLIENT_ID ,
domain: process . env . AUTH0_DOMAIN ,
};
let auth0Client ;
const initialState = {
isAuthenticated: false ,
isInitialized: false ,
user: null ,
};
const AuthContext = createContext ({
... initialState ,
method: 'auth0' ,
signin : () => Promise . resolve (),
logout : () => Promise . resolve (),
});
function AuthProvider ({ children }) {
const [ state , dispatch ] = useReducer ( reducer , initialState );
useEffect (() => {
const initialize = async () => {
try {
auth0Client = new Auth0Client ({
domain: auth0Config . domain ,
clientId: auth0Config . client_id ,
authorizationParams: {
redirect_uri: window . location . origin
},
});
await auth0Client . checkSession ();
const isAuthenticated = await auth0Client . isAuthenticated ();
if ( isAuthenticated ) {
const user = await auth0Client . getUser ();
dispatch ({
type: 'INITIALIZE' ,
payload: { isAuthenticated , user },
});
}
} catch ( err ) {
console . error ( err );
dispatch ({
type: 'INITIALIZE' ,
payload: { isAuthenticated: false , user: null },
});
}
};
initialize ();
}, []);
const signin = async () => {
await auth0Client . loginWithPopup ();
const isAuthenticated = await auth0Client . isAuthenticated ();
if ( isAuthenticated ) {
const user = await auth0Client . getUser ();
dispatch ({ type: 'LOGIN' , payload: { user } });
}
};
const logout = () => {
auth0Client . logout ();
dispatch ({ type: 'LOGOUT' });
};
return (
< AuthContext.Provider
value = { {
... state ,
method: 'auth0' ,
user: {
id: state ?. user ?. sub ,
photoURL: state ?. user ?. picture ,
email: state ?. user ?. email ,
displayName: state ?. user ?. name ,
},
signin ,
logout ,
} }
>
{ children }
</ AuthContext.Provider >
);
}
To switch to Auth0, update the import in guards/authGuard/UseAuth.js to use Auth0Context instead of FirebaseContext.
Authentication State Management
Redux Auth Slice
import { createSlice } from '@reduxjs/toolkit' ;
import moment from 'moment' ;
const initialState = {
isAuthenticated: false ,
isInitialized: false ,
user: null ,
};
const authSlice = createSlice ({
name: 'auth' ,
initialState ,
reducers: {
authStateChanged ( state , action ) {
const { isAuthenticated , user } = action . payload ;
if ( user !== null ) {
const convertTimestamp = ( timestamp ) => {
if ( timestamp && timestamp . seconds ) {
const date = new Date (
timestamp . seconds * 1000 +
timestamp . nanoseconds / 1000000
);
return moment ( date ). format ( 'DD/MM/YYYY - hh:mm:ss a' );
}
return null ;
};
user . date . last_update = convertTimestamp ( user . date ?. last_update );
user . date . last_access = convertTimestamp ( user . date ?. last_access );
}
state . isAuthenticated = isAuthenticated ;
state . isInitialized = true ;
state . user = user ;
},
},
});
export const { authStateChanged } = authSlice . actions ;
export default authSlice . reducer ;
Using Authentication State
import useAuth from 'src/guards/authGuard/UseAuth' ;
function MyComponent () {
const { user , isAuthenticated , signin , logout } = useAuth ();
if ( ! isAuthenticated ) {
return < div > Please log in </ div > ;
}
return (
< div >
< h1 > Welcome, { user . name } </ h1 >
< p > Role: { user . account_type } </ p >
< button onClick = { logout } > Sign Out </ button >
</ div >
);
}
Route Protection
Auth Guard
Protects routes that require authentication:
import { useNavigate } from 'react-router-dom' ;
import useAuth from './UseAuth' ;
import { useEffect } from 'react' ;
const AuthGuard = ({ children }) => {
const { isAuthenticated } = useAuth ();
const navigate = useNavigate ();
useEffect (() => {
if ( ! isAuthenticated ) {
navigate ( '/auth/login' , { replace: true });
}
}, [ isAuthenticated , navigate ]);
return children ;
};
export default AuthGuard ;
Guest Guard
Prevents authenticated users from accessing public routes:
import { useNavigate } from 'react-router-dom' ;
import useAuth from './UseAuth' ;
import { useEffect } from 'react' ;
const GuestGuard = ({ children }) => {
const { isAuthenticated } = useAuth ();
const navigate = useNavigate ();
useEffect (() => {
if ( isAuthenticated ) {
navigate ( '/' , { replace: true });
}
}, [ isAuthenticated , navigate ]);
return children ;
};
Permission Guard
Enforces role-based permissions using CASL:
import { Can } from '@casl/react' ;
import { useNavigate } from 'react-router-dom' ;
import { useAbility } from '../contexts/AbilityContext' ;
const PermissionGuard = ({ action , subject , children }) => {
const ability = useAbility ();
const navigate = useNavigate ();
return (
< Can I = { action } a = { subject } ability = { ability } >
{ ( allowed ) =>
allowed ? children : navigate ( '/auth/permissions' , { replace: true })
}
</ Can >
);
};
Session Management
Persistent Sessions
Authentication state is persisted using Redux Persist:
const persistConfig = {
key: 'root' ,
storage ,
whitelist: [ 'auth' , 'customizer' , 'setup' ]
};
Session Tracking
Each authentication event updates user tracking data:
User Login
Firebase auth state listener detects authentication
IP Detection
Fetch user’s IP address from ipapi.co
Firestore Update
Update last_access timestamp and last_IP in Firestore
State Sync
Dispatch updated user data to Redux store
Auto-logout on Status Change
Users are automatically logged out if their account status changes:
if ( querySnapshot . data ()?. status !== true ) {
logout ();
}
User Profile Management
Updating User Profile
const UpdateUser = ( oldUser , newUser ) => {
dispatch ( authStateChanged ({
isAuthenticated: true ,
user: {
id: oldUser . id ,
account_type: oldUser . account_type ,
avatar: newUser . image ?? oldUser . avatar ,
... newUser ,
},
}));
};
User Data Structure
interface User {
uid : string ;
email : string ;
name : string ;
avatar ?: string ;
account_type : 'Administrador' | 'Cliente' | 'Coordinador' | 'Contador' | 'Soporte' ;
phone ?: string ;
address ?: {
line : string ;
zipcode : string ;
city : string ;
country : object ;
};
status : boolean ;
date : {
created : string ;
last_update : string ;
last_access : string ;
};
last_IP : string ;
country_calling_code : string ;
}
Security Best Practices
All login attempts validate against the u_clients collection using a Cloud Function to prevent unauthorized access.
User accounts have a status field that must be true for authentication to succeed.
Each login is tracked with IP address and timestamp for security auditing.
Multi-layered guards ensure only authenticated and authorized users access protected routes.
Common Authentication Flows
Standard Login Flow
// 1. User submits login form
await validateUserPlatform ({ platform: 'u_clients' , email });
// 2. If validated, sign in with Firebase
await signin ( email , password );
// 3. Firebase auth listener triggers
firebase . auth (). onAuthStateChanged ( async ( user ) => {
// 4. Fetch user profile from Firestore
const profile = await Firestore . collection ( 'u_clients' ). doc ( user . uid ). get ();
// 5. Verify status
if ( profile . data ()?. status !== true ) {
logout ();
return ;
}
// 6. Track access
await Firestore . collection ( 'u_clients' ). doc ( user . uid ). update ({
"date.last_access" : firebase . firestore . FieldValue . serverTimestamp (),
});
// 7. Update Redux state
dispatch ( authStateChanged ({ isAuthenticated: true , user: profile . data () }));
});
Password Reset Flow
// 1. User requests password reset
await ResetPassword ( email );
// 2. Firebase sends email with reset link
// 3. User clicks link and resets password
// 4. User can now login with new password
Error Handling
Common Authentication Errors
The email address is badly formatted
The password or email is incorrect
Too many failed login attempts
The user account has been disabled
Next Steps
Permissions Learn about CASL-based role and permission system
Redux Store Explore Redux state management and auth slice
User Components Discover user management components
Overview Review the complete API architecture