Overview
The Auction Platform implements a robust authentication system with role-based access control (RBAC), supporting two user roles: ORGANIZER and TEAM_ADMIN. The authentication flow is built using Zustand for state management and includes automatic session initialization and redirect logic.
Authentication Flow
User Initialization
When the app loads, the useAuthStore automatically initializes by calling /auth/me to check if a valid session exists.
Login/Signup
Users can authenticate through the login or signup forms, which validate credentials and establish a session.
Session Management
Upon successful authentication, user data and authentication state are stored in the Zustand store.
Route Protection
Protected routes check authentication status and redirect unauthenticated users to login.
User Types and Roles
The system supports two distinct user roles:
// src/features/auth/types/auth.types.ts
export type User = {
id : number ;
email : string ;
role : 'ORGANIZER' | 'TEAM_ADMIN' ;
is_onboarded : boolean ;
}
Role Descriptions
ORGANIZER Primary users who can create and manage auctions. Organizers go through an onboarding process to set up their profile and company information.
TEAM_ADMIN Administrative users with elevated permissions to manage teams and organizational settings.
Authentication State
The authentication state is managed globally using Zustand and includes:
src/features/auth/types/auth.types.ts:16
export type AuthState = {
isAuthenticated : boolean ; // Whether user is logged in
isOnboarded : boolean ; // Whether user completed onboarding
user : User | null ; // Current user data
initialized : boolean ; // Whether auth state has been initialized
}
Schema Definition
The login form uses a schema-driven approach with built-in validation:
src/features/auth/Schema/auth.schema.ts:6
export const LoginFormSchema : ( AuthFieldType & {
leftIcon ?: LucideIcon ,
rightIcon ?: LucideIcon
})[] = [
{
id: "email" ,
label: "Email" ,
type: "email" ,
placeholder: "Enter your email" ,
rightIcon: Mail ,
fieldValidators: [
{ type: "required" }
]
},
{
id: "password" ,
label: "Password" ,
type: "password" ,
placeholder: "Enter your password" ,
leftIcon: Key ,
fieldValidators: [
{ type: "required" },
{ type: "minLength" , constraints: { minLength: 8 } },
]
}
]
Login Component
The login form uses the useFormEngine hook for form state management and validation:
src/features/auth/components/LoginForm.tsx:12
export default function LoginForm () {
const { formData , onChange , errors } = useFormEngine < AuthFieldType >( LoginFormSchema )
return (
< section className = { styles . authcontainer } >
< Typography as = "h1" weight = "bold" > Access Your Account </ Typography >
{ LoginFormSchema . map (( field ) => (
< TextField
key = { field . id }
id = { field . id }
value = { formData [ field . id ] ?? "" }
type = { field . type }
label = { field . label }
placeholder = { field . placeholder }
onChange = { ( e ) => onChange ( e , field . id ) }
LeftIcon = { field . leftIcon }
RightIcon = { field . rightIcon }
error = { errors [ field . id ] ? true : false }
helperText = { errors [ field . id ] ? errors [ field . id ] : "" }
/>
)) }
< Button variant = "primary" > Submit </ Button >
< Typography as = "p" weight = "light" size = "text-md" >
Don't have an Account? < Link to = "/signup" > Sign up </ Link >
</ Typography >
</ section >
)
}
Schema Definition
The signup form includes password confirmation:
src/features/auth/Schema/auth.schema.ts:31
export const SignupFormSchema : ( AuthFieldType & { leftIcon ?: LucideIcon })[] = [
{
id: "email" ,
label: "Email" ,
type: "email" ,
placeholder: "Enter your email" ,
leftIcon: Mail ,
fieldValidators: [
{ type: "required" }
]
},
{
id: "password" ,
label: "Password" ,
type: "password" ,
placeholder: "Enter your password" ,
leftIcon: Key ,
fieldValidators: [
{ type: "required" },
{ type: "minLength" , constraints: { minLength: 8 } }
]
},
{
id: "cnfpassword" ,
label: "Confirm Password" ,
type: "password" ,
placeholder: "Confirm your password" ,
leftIcon: Key ,
fieldValidators: [
{ type: "required" },
]
}
]
Password Requirements
Passwords must be at least 8 characters long. Additional validation rules can be added to the schema’s fieldValidators array.
Authentication Service
The authentication service handles API communication and state updates:
Login
Logout
Session Init
src/features/auth/services/auth.service.ts:15
export async function login ( email : string , password : string ) {
const data : LoginResponse = await apiPOST ( "/auth/login" , {
body: JSON . stringify ({ email , password })
});
useAuthStore . getState (). login ( data . user );
return data . user
}
src/features/auth/services/auth.service.ts:27
export async function logout () {
useAuthStore . getState (). logout ();
await apiPOST ( "/auth/logout" , { method: "POST" });
}
src/app/store/auth/auth.store.ts:17
init : async () => {
if ( get (). initialized ) return ;
try {
const user : User = await apiGET ( "/auth/me" );
set ({
user ,
isAuthenticated: true ,
isOnboarded: user . is_onboarded ,
initialized: true ,
});
} catch {
set ({
user: null ,
isAuthenticated: false ,
isOnboarded: false ,
initialized: true ,
});
}
}
Auth Store (Zustand)
The global authentication store manages user state throughout the application:
src/app/store/auth/auth.store.ts:11
export const useAuthStore = create < AuthStore >(( set , get ) => ({
isAuthenticated: false ,
isOnboarded: false ,
user: null ,
initialized: false ,
init : async () => { /* ... */ },
login : ( user ) =>
set ({
user ,
isAuthenticated: true ,
isOnboarded: user . is_onboarded ,
initialized: true ,
}),
logout : () =>
set ({
user: null ,
isAuthenticated: false ,
isOnboarded: false ,
initialized: true ,
}),
}));
Route Protection
Routes are protected using TanStack Router’s beforeLoad hook:
src/routes/_without-navbar/login.tsx:5
export const Route = createFileRoute ( '/_without-navbar/login' )({
beforeLoad : async () => {
const { isAuthenticated , isOnboarded } = useAuthStore . getState ();
// Allow access if not authenticated
if ( ! isAuthenticated ) {
return ;
}
// Redirect to onboarding if not completed
if ( ! isOnboarded ) {
throw redirect ({ to: "/onboarding" });
}
// Redirect to dashboard if already authenticated
throw redirect ({ to: "/dashboard" });
},
component : () => < AuthLayout type = "login" /> ,
})
API Client Configuration
The API client automatically handles authentication:
Request Handler with Auth
export async function request ( endpoint : string , options : RequestInit = {}) {
const response = await fetch ( ` ${ API_BASE_URL }${ endpoint } ` , {
... fetchOptions ,
credentials: "include" , // Include cookies for session
headers ,
signal: controller . signal
})
// Auto-logout on 401
if ( response . status === 401 ) {
useAuthStore . getState (). logout ();
throw new Error ( "Unauthorized" );
}
if ( ! response . ok ) {
let messsage = "API request failed"
try {
const error = await response . json (). catch (() => ({}))
messsage = error . error || messsage
} catch { }
throw new Error ( messsage )
}
return response . status === 204 ? null : response . json ();
}
The platform uses a custom form engine that provides real-time validation:
src/app/hooks/useFormEngine.tsx:9
export default function useFormEngine < T extends BaseField >( formSchema : T []) {
const [ formData , setFormData ] = useState < Record < string , string >>({})
const [ errors , setErrors ] = useState < Record < string , string >>({})
// Compile schema with attached validators
const compiledSchema = formSchema . map ( attachValidators )
const onChange = ( e : React . ChangeEvent < HTMLInputElement >, fieldId : string ) => {
const val = e . target . value ;
setFormData (( prev ) => ({ ... prev , [fieldId]: val }));
const fieldSchema = getFieldSchema ( fieldId );
if ( ! fieldSchema ) return
// Validate field on change
const error = validateFields ( val , fieldSchema );
setErrors (( prev ) => ({ ... prev , [fieldId]: error }))
}
return { formData , onChange , errors }
}
Best Practices
Always call useAuthStore.getState().init() on app startup
Handle 401 responses by automatically logging out users
Use credentials: "include" for cookie-based sessions
Check both isAuthenticated and isOnboarded states
Redirect unauthenticated users to login
Redirect non-onboarded users to onboarding flow
API Endpoints
Authenticate user with email and password Request Body: Response: {
"token" : "jwt_token_here" ,
"user" : {
"id" : 1 ,
"email" : "[email protected] " ,
"role" : "ORGANIZER" ,
"is_onboarded" : false
}
}
End user session and clear authentication state
Get current authenticated user information Response: