Skip to main content

Authentication & Session Management

BonardaHR supports dual authentication modes:

  1. Microsoft Entra ID (Azure Microsoft Authentication Library (MSAL) Single Sign-On (SSO)): Enterprise Single Sign-On using OAuth 2.0 PKCE.
  2. 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:

  1. AuthProvider checks localStorage.getItem('token').
  2. If present, it executes authService.getMe() to fetch current user permissions and profile.
  3. While loading, isLoading is set to true (preventing premature redirects).
  4. 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);
}
);