Skip to content

Authentication and access control

Authentication and access control

What this system does

The repository supports two overlapping authentication paths:

The app still exports both through src/auth/index.js, and App.jsx/AdminRoute.jsx choose between them based on configuration and runtime availability. The MSAL path is the more complete production flow: it handles Microsoft sign-in, backend token exchange, tenant restoration, consent status, and admin role resolution.

Runtime flow

sequenceDiagram
participant Browser
participant App as App.jsx
participant MSAL as MsalAuthProvider
participant Backend as backendAuthService
participant Consent as consentService
participant Tenant as tenantService
Browser->>App: load application
App->>MSAL: read auth state / consent gate
MSAL->>Backend: exchange Microsoft token after redirect or popup login
Backend-->>MSAL: backend JWT, refresh token, user profile, role, tenant ids
MSAL->>Consent: restore or check consent state
MSAL->>Tenant: fetch tenants and restore selected tenant
Tenant-->>MSAL: tenant list + tenant profile
MSAL-->>App: authenticated user, role, consent, tenant context

Legacy Firebase auth

AuthContext is still present for compatibility. It listens to Firebase auth state, exposes user, role, isAdmin, loading/error flags, and sign-in/sign-out helpers, and logs failures through loggingService. In this repository, it is best treated as a compatibility layer rather than the primary identity model.

Important constraints

  • Admin detection via email lists is intentionally removed; the code comments note that admin identity should come from Azure AD groups or backend role state instead.
  • This context exists mainly to keep older surfaces working while the MSAL path is the authoritative one.

MSAL auth and backend exchange

MsalAuthContext owns the main authentication state machine.

Inputs it consumes

  • MSAL accounts and interaction state from @azure/msal-react.
  • Group IDs from msalConfig.
  • BackendAuthContext for token exchange.
  • tenantService and consentService for backend state restoration.

State it owns

  • user, role, and groups from Microsoft identity data.
  • backendUser, backendTokenExchanged, tenants, currentTenantId, and tenantBranding.
  • consent flags: consentGiven plus local and backend fallbacks.

How role resolution works

Role resolution is deliberately layered:

  1. tenant branding membership role,
  2. selected tenant membership role,
  3. selected tenant role,
  4. backend user role,
  5. backend user membership role,
  6. backend is_admin,
  7. MSAL role,
  8. fallback to user.

The authoritative resolver is resolveEffectiveRole.

Consent is a first-class gate in the app lifecycle. App.jsx checks consent state before allowing entry into the main workspace, and MsalAuthContext restores consent from local storage or backend state after sign-in and refresh.

Relevant service behavior:

Tenant selection and persistence

Tenant context is restored after authentication if the backend session exists.

Tenant lifecycle

  • Fetch the available tenants for the user.
  • Restore the selected tenant from session storage if it still exists.
  • Otherwise auto-select the first tenant.
  • Load the current tenant profile/branding.
  • Persist the active tenant through backend auth helpers so future requests include the tenant header.

tenantService owns the fetch/select/switch/profile APIs. MsalAuthContext consumes it at initialization time and whenever tenant branding needs to be refreshed.

Admin access

AdminRoute is the gate for admin pages. It considers:

  • Firebase admin state,
  • Azure MSAL admin state, and
  • the effective role produced by resolveEffectiveRole().

[canAccessAdminPortal](../../src/utils/rbac.js) permits access for admin/manager/platform-admin users.

Storage and state boundaries

This auth stack uses persistent browser storage intentionally:

  • session storage for backend JWT/user/tenant state and selected tenant identity,
  • local storage for consent flags.

That split matters:

  • session-scoped values are meant to evaporate when the browser session ends,
  • consent must survive logout/re-login until revoked.

Extension points

When changing auth behavior, the canonical places are:

  • src/auth/msalConfig.js for Azure app registration values and group IDs,
  • src/auth/backendAuthService.js for exchange/token lifecycle,
  • src/services/consentService.js for consent API semantics,
  • src/services/tenantService.js for tenant fetch/select behavior,
  • src/utils/rbac.js for admin access rules.

Focused tests

  • tests/unit/rbac.test.js proves admin access and effective-role precedence.
  • tests/contract/client.test.js proves auth header propagation and tenant headers in API requests.
  • The Playwright workflow checks in e2e/tests/workflow/verify-agent-preflight.spec.js and related preflight specs exercise authenticated portal flows.