frontend_nyla/docs/LOGIN_AND_PRIVILEGE_FLOW.md

12 KiB

Login and Privilege Flow Documentation

Overview

This document describes the complete login flow, including user data fetching, privilege checking, and language synchronization.

Updated Login Flow (Post-Fix)

1. Login Process

Local Authentication (useAuth in useAuthentication.ts)

User enters credentials → POST /api/local/login → Success
    ↓
✅ Tokens stored in httpOnly cookies
✅ authenticationAuthority saved to localStorage
    ↓
🔄 IMMEDIATE user data fetch: GET /api/local/me
    ↓
✅ User data cached in localStorage ('currentUser')
    - Includes: username, privilege, language, etc.
    - Language is part of user data (NO separate storage!)
    ↓
Navigate to Home page

Microsoft Authentication (useMsalAuth in useAuthentication.ts)

User clicks Microsoft login → Popup opens → Microsoft OAuth flow
    ↓
✅ Tokens stored in httpOnly cookies
✅ authenticationAuthority saved to localStorage
    ↓
⏳ Wait 500ms for cookie propagation
    ↓
🔄 IMMEDIATE user data fetch: GET /api/msft/me
    ↓
✅ User data cached in localStorage ('currentUser')
✅ Language setting synced to localStorage ('language')
    ↓
Navigate to Home page

Google Authentication (useGoogleAuth in useAuthentication.ts)

User clicks Google login → Popup opens → Google OAuth flow
    ↓
✅ Tokens stored in httpOnly cookies
✅ authenticationAuthority saved to localStorage
    ↓
⏳ Wait 500ms for cookie propagation
    ↓
🔄 IMMEDIATE user data fetch: GET /api/google/me
    ↓
✅ User data cached in localStorage ('currentUser')
✅ Language setting synced to localStorage ('language')
    ↓
Navigate to Home page

2. Home Page Load (Home.tsx)

Home page mounts
    ↓
useCurrentUser() hook called
    ↓
Checks localStorage for cached user data
    ↓
If cached: Uses cached data (instant)
If not cached: Fetches from API (with loading state)
    ↓
User data available
    ↓
PageManager receives user data context

3. Language Synchronization (LanguageContext.tsx)

The language context now follows a priority system:

Priority Order:

  1. User profile language (from localStorage('currentUser').language - synced from backend)
  2. Browser language (from navigator.language - fallback if no user data)

Language Loading:

LanguageProvider mounts
    ↓
Check currentUser in localStorage
    ↓
If user.language exists: Use user.language ✅
    ↓
Else: Use browser language (fallback)
    ↓
Load translations for selected language

Language Updates (Settings Flow):

User changes language in settings
    ↓
1. Update backend user profile (PUT /api/users/{id})
    ↓
2. Backend returns updated user data
    ↓
3. Update localStorage('currentUser') with new data ✅
    ↓
4. Call setLanguage() to load new translations
    ↓
5. Trigger 'userInfoUpdated' event
    ↓
LanguageContext syncs and UI updates

4. Privilege Checking System

Where Privileges Are Checked:

A. Page Level (PageManager.tsx)

// Line 29-40 in PageManager.tsx
const checkPageAccess = async (pageData: GenericPageData): Promise<boolean> => {
    if (!pageData.privilegeChecker) {
        return true; // No checker = accessible to all
    }
    
    try {
        return await pageData.privilegeChecker();
    } catch (error) {
        console.error(`Error checking page access for ${pageData.path}:`, error);
        return false;
    }
};

B. Privilege Checkers (privilegeCheckers.ts)

All privilege checkers read from localStorage.getItem('currentUser'):

const getCurrentUserPrivilege = (): string | null => {
    try {
        const userData = localStorage.getItem('currentUser');
        if (userData) {
            const user = JSON.parse(userData);
            return user.privilege || null;
        }
        return null;
    } catch (error) {
        console.error('Error getting user privilege:', error);
        return null;
    }
};

Available Privilege Checkers:

  • privilegeCheckers.adminRole - For admin and sysadmin users
  • privilegeCheckers.sysadminRole - For sysadmin only
  • privilegeCheckers.userRole - For user, admin, and sysadmin
  • privilegeCheckers.viewerRole - For all authenticated users
  • privilegeCheckers.speechSignup - For speech feature access
  • privilegeCheckers.alwaysAllow - For public pages
  • privilegeCheckers.neverAllow - For disabled features

Privilege Check Flow:

PageManager renders page
    ↓
checkPageAccess(pageData)
    ↓
pageData.privilegeChecker() called
    ↓
Reads from localStorage('currentUser')
    ↓
Checks user.privilege against required privileges
    ↓
Returns true/false
    ↓
If true: Page renders
If false: Error component shows

5. Complete Flow Diagram

┌─────────────────────────────────────────────────────────────┐
│                         LOGIN                                │
│  (Local/Microsoft/Google)                                    │
└──────────────────┬──────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────────────────────────┐
│  ✅ Set httpOnly cookies (backend)                           │
│  ✅ Save auth_authority to localStorage                      │
│  🔄 IMMEDIATELY fetch user data: GET /api/*/me              │
│  ✅ Cache user data in localStorage('currentUser')          │
│  ✅ Sync language to localStorage('language')               │
└──────────────────┬──────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────────────────────────┐
│                   Navigate to Home                           │
└──────────────────┬──────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────────────────────────┐
│                   Home.tsx Mounts                            │
│  - useCurrentUser() → Reads from localStorage (instant!)    │
│  - LanguageProvider → Reads user.language (instant!)        │
└──────────────────┬──────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────────────────────────┐
│                 PageManager Renders                          │
│  - Gets currentLanguage from LanguageContext                 │
│  - Checks page privileges (reads from localStorage)          │
│  - Passes language to PageRenderer                           │
└──────────────────┬──────────────────────────────────────────┘
                   ↓
┌─────────────────────────────────────────────────────────────┐
│                PageRenderer Displays Page                    │
│  - Uses user's language for all text                         │
│  - All privilege checks use cached user data                 │
└─────────────────────────────────────────────────────────────┘

Key Changes Made

Fixed Issues:

  1. User data is now fetched IMMEDIATELY after login

    • Previously: Fetched only when Home.tsx mounted
    • Now: Fetched right after successful authentication
    • Location: src/hooks/useAuthentication.ts (lines 65-88 for local, 258-297 for Microsoft, 727-753 for Google)
  2. Language is synced from user profile

    • Previously: Loaded from localStorage or browser only
    • Now: Prioritizes user.language from API response
    • Location: src/contexts/LanguageContext.tsx (lines 42-108)
  3. Language is passed to PageRenderer

    • Previously: Default 'de' was used
    • Now: Current language from context is passed
    • Location: src/core/PageManager/PageManager.tsx (line 104)
  4. Privilege checks use cached user data

    • User data is available immediately in localStorage
    • No race conditions between page load and user data fetch
    • Location: src/utils/privilegeCheckers.ts (lines 4-21)

📝 Important Notes:

  1. OAuth Cookie Delay: Microsoft and Google auth have a 500ms delay before fetching user data to ensure cookies are properly set by the browser.

  2. Error Handling: If user data fetch fails after login, the user is still navigated to the home page, but will see a loading/error state there.

  3. Cache Strategy: User data is cached in localStorage for instant access, but is also refreshed on each page load via useCurrentUser() hook.

  4. Language Updates: When a user updates their language in settings, the system:

    • Updates backend user profile
    • Triggers 'userInfoUpdated' event
    • LanguageContext listens and syncs the new language
    • All components using useLanguage() automatically update

API Endpoints Used

Endpoint Purpose When Called
POST /api/local/login Local authentication User submits login form
GET /api/local/me Get current user (local) Immediately after local login + on Home.tsx mount
GET /api/msft/me Get current user (Microsoft) Immediately after Microsoft login + on Home.tsx mount
GET /api/google/me Get current user (Google) Immediately after Google login + on Home.tsx mount

Testing the Flow

To verify the flow is working correctly:

  1. Login Test:

    - Clear localStorage
    - Log in with any method
    - Check console for: "🔄 Fetching user data immediately after login..."
    - Check console for: "✅ User data fetched and cached"
    - Verify localStorage has 'currentUser' and 'language' keys
    
  2. Language Test:

    - Log in
    - Check console for: "🌍 Using language from user data: [language]"
    - Change language in settings
    - Verify UI updates immediately
    
  3. Privilege Test:

    - Log in as user with different privilege levels
    - Navigate to admin pages
    - Verify access based on privilege
    - Check console for: "🔍 Checking role privilege" logs
    

Troubleshooting

Issue: Pages show "Access denied" after login

Solution: Check if user data is properly cached in localStorage. Look for console errors in user data fetch.

Issue: Wrong language is displayed

Solution: Verify that user.language exists in the API response. Check browser console for language loading logs.

Issue: OAuth login doesn't fetch user data

Solution: Check if the 500ms delay is sufficient for your environment. Increase delay if needed in useAuthentication.ts.

  • src/hooks/useAuthentication.ts - Login logic and immediate user fetch
  • src/hooks/useUsers.ts - User data management
  • src/contexts/LanguageContext.tsx - Language management
  • src/core/PageManager/PageManager.tsx - Page routing and privilege checking
  • src/core/PageManager/PageRenderer.tsx - Page rendering with language
  • src/utils/privilegeCheckers.ts - Privilege checking utilities
  • src/pages/Home/Home.tsx - Main application entry after login