Skip to content
Paulo Gomes da Cruz Junior edited this page Oct 23, 2026 · 3 revisions

Roles and Access Validation (Developer Guide)

This document describes how role-based access is implemented in the frontend, including provider-specific constraints, abstract-role client requirements, rule-evaluation order, and how to safely extend the system.

Source of truth

The access rules are centralized in:

  • src/context/auth/userAccessValidation.ts

Routes and pages that consume this logic:

  • src/routing/auth.ts — computeAuthRedirect wraps getUserAccessStatus for the _authenticated layout; hasAnyRole performs any-of role checks for the tier layouts
  • src/routes/_authenticated.tsx — auth + status gate layout
  • src/routes/_authenticated/_admin.tsx, src/routes/_authenticated/_client-access.tsx — role-tier layouts
  • src/pages/NoRole/index.tsx
  • src/pages/RoleError/index.tsx

Primary role model definitions:

  • src/context/auth/types.ts

Role model used by the app

FamLoginUser.roles contains both:

  1. Assigned business roles (for example VIEWER, SUBMITTER, AREA, etc.)
  2. Provider marker roles (IDIR or BCeID) appended during token parsing

Provider marker roles are metadata, not assignable business roles. Validation intentionally excludes them from assigned-role checks.

Rule evaluation contract

Rules are evaluated in deterministic order. First matching violation wins and drives redirect/message behavior.

Rule 1: No assigned roles

GIVEN user has any idpProvider
WHEN user has no assigned roles (after removing provider marker roles)
THEN redirect to /no-role

Implementation detail:

  • getAssignedRoles(user) filters out IDIR and BCeID marker roles.
  • If assignedRoles.length === 0, status is kind: 'no-role'.

Rule 2: IDIR cannot have multiple assigned roles

GIVEN user has idpProvider = IDIR
WHEN user has more than one assigned role
THEN redirect to /unauthorized?reason=IDIR_MULTIPLE_ROLES with message:
This account is not permitted to have multiple roles.

Rule 3: BCeID cannot have ADMIN/AREA/DISTRICT

GIVEN user has idpProvider = BCEIDBUSINESS
WHEN user has at least one assigned role in { ADMIN, AREA, DISTRICT }
THEN redirect to /unauthorized?reason=BCEID_ASSIGNED_ROLES with message:
This account is not permitted to have assigned roles.

Rule 4 (strict): every abstract role must have at least one client

GIVEN user has any idpProvider
WHEN user has an abstract role (currently VIEWER or SUBMITTER) with an empty client list
THEN redirect to /unauthorized?reason=CLIENT_ACCESS_REQUIRES_CLIENT with message:
This account requires at least one client

Important:

  • This is an individual role check, not a consolidated client pool check.
  • Example that now correctly fails:
    • SUBMITTER: ['100']
    • VIEWER: []

Rule 5: VIEWER and SUBMITTER cannot overlap clients

GIVEN user has any idpProvider
WHEN user has both VIEWER and SUBMITTER AND at least one shared client between those two roles
THEN redirect to /unauthorized?reason=CONFLICTING_CLIENT_ACCESS_ROLES with message:
This account has conflicting client access roles

Redirect and UI behavior

  1. The _authenticated layout (src/routes/_authenticated.tsx) runs the pure computeAuthRedirect (src/routing/auth.ts), which calls getUserAccessStatus(user) after the authentication check.
  2. For kind: 'no-role' it redirects to /no-role.
  3. For kind: 'role-error' it redirects to redirectTo (for example /unauthorized?reason=...), except when already on /unauthorized (loop guard).
  4. RoleError reads reason from query params and resolves the message via getAccessViolationMessage(reason).

Why provider marker roles exist

During JWT parsing, the app appends one provider marker role (IDIR or BCeID) so downstream logic can quickly identify IdP context from role data. Because this marker is not an assigned business role, all business-role validation must use getAssignedRoles(...).

How to add a new role

When introducing new roles, update both type definitions and validation behavior.

1) Update role definitions

File: src/context/auth/types.ts

  • Add role to Role enum.
  • Classify role in roleTypeMap as RoleType.ABSTRACT or RoleType.CONCRETE.

Role type classification matters:

  • ABSTRACT roles are expected to have one or more client IDs.
  • CONCRETE roles are identity/scoped roles that are not client-list based.

2) Update token parsing (if required)

File: src/context/auth/authUtils.ts

  • Ensure extractRoles(...) correctly recognizes and maps the new role from Cognito groups.

3) Add or adjust access rules

File: src/context/auth/userAccessValidation.ts

  • Add a new code in accessViolationMessages.
  • Add/adjust rule logic in getUserAccessStatus(...).
  • Keep evaluation order intentional (more general rules can mask later specific rules).

4) Add tests for behavior and precedence

File: src/context/auth/userAccessValidation.unit.test.ts

  • Add positive and negative cases.
  • Add precedence tests when a user could match multiple violations.

How to add a new violation message

  1. Add a key/value in accessViolationMessages in userAccessValidation.ts.
  2. Return that code from getUserAccessStatus(...).
  3. Redirect using /unauthorized?reason=<NEW_CODE>.
  4. Add unit tests:
    • Validator returns expected code and redirect.
    • RoleError page renders expected mapped message.

Rule-order guidance

Before adding a new rule, decide its precedence relative to existing rules:

  • Put it earlier if it should supersede others.
  • Put it later if it should only run after base eligibility checks.

Current order is designed so provider restrictions are evaluated before client-association constraints.

Operational tips

  • Keep all access-rule logic centralized in userAccessValidation.ts.
  • Avoid duplicating role checks in route components or pages.
  • Use reason codes (not raw text) for redirects so messages stay configurable.
  • Always normalize client lists (trim, remove empty values) before comparing.

Test coverage currently expected

At minimum, maintain coverage for:

  • provider markers excluded from assigned roles
  • no-role redirect
  • IDIR multi-role rejection
  • BCeID forbidden concrete-role rejection
  • strict abstract-role client requirement (per-role)
  • overlapping VIEWER/SUBMITTER client rejection
  • default/unknown reason behavior for RoleError message mapping

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally