Authentication & Session Management
BonardaHR supports dual authentication modes:
- Microsoft Entra ID (Azure Microsoft Authentication Library (MSAL) Single Sign-On (SSO)): Enterprise Single Sign-On using OAuth 2.0 PKCE.
- Dev Login (Mock / Seed Mode): Fast developer login for local development and QA testing.
Microsoft Entra ID (MSAL) Flow​
In production, authentication utilizes @azure/msal-browser and @azure/msal-react:
[ User clicks 'Sign in with Microsoft' ]
│
â–¼
[ MSAL Redirect to login.microsoftonline.com ]
│
â–¼
[ User Authenticates with Azure AD credentials ]
│
â–¼
[ Redirect to /auth/callback with Auth Code ]
│
â–¼
[ AuthCallbackPage exchanges code for Backend JWT ]
│
â–¼
[ Token saved to localStorage('token') & AuthContext ]
│
â–¼
[ User redirected to / dashboard ]
Dev Login Mode​
When VITE_DEV_LOGIN_ENABLED=true, the LoginPage component fetches available mock employees from GET /api/v1/auth/dev-employees:
// features/auth/components/LoginPage.tsx
const handleDevLogin = async (employeeId: string) => {
try {
const response = await authService.devLogin({ employeeId });
login(response.token, response.user);
navigate('/');
} catch (err) {
showAuthErrorToast(err);
}
};
Auth State & Context (AuthProvider.tsx)​
The authentication state is managed globally by AuthProvider:
// features/auth/types/auth.types.ts
export interface AuthUser {
id: string;
email: string;
name: string;
avatarUrl?: string;
roles: string[];
permissions: string[];
employeeId?: string;
}
export interface AuthContextType {
user: AuthUser | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (token: string, user: AuthUser) => void;
logout: () => void;
hasPermission: (permission: string | string[]) => boolean;
hasRole: (role: string | string[]) => boolean;
}
Initial Hydration​
Upon page load or refresh:
AuthProvidercheckslocalStorage.getItem('token').- If present, it executes
authService.getMe()to fetch current user permissions and profile. - While loading,
isLoadingis set totrue(preventing premature redirects). - If token is invalid or expired, token is removed and state resets to unauthenticated.
Token Interceptor & Auto Logout​
The Axios HTTP client automatically includes the Bearer token in outgoing requests and catches 401 Unauthorized responses:
// api/apiClient.ts
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (
error.response?.status === 401 &&
!window.location.pathname.startsWith('/login') &&
!window.location.pathname.startsWith('/auth/callback')
) {
localStorage.removeItem('token');
sessionStorage.clear();
window.location.href = '/login';
}
return Promise.reject(error);
}
);