Define AccessDenialService interface and role-block contract
epic-no-access-screen-access-control-task-001 — Create the Dart abstract interface for AccessDenialService defining the isRoleBlocked stream/notifier, the getAdminPortalUrl method, and the blockedRoles set. Establish the data contract between the service and its consumers before any implementation begins.
Acceptance Criteria
Technical Requirements
Implementation Notes
This is a contract-definition task — no business logic is implemented here. Place the file at lib/services/access_denial_service.dart (or equivalent project service path). Use Dart's abstract class pattern for the interface. Define isRoleBlocked as a Stream
Define blockedRoles as UnmodifiableSetView
Testing Requirements
No runtime tests needed for an abstract interface definition. Write one unit test file that creates a mock implementation of AccessDenialService (using Mockito or manual stub) to verify the interface is implementable and all methods are callable as specified. Assert that a mock returning isRoleBlocked = Stream.value(true) and getAdminPortalUrl() = Future.value('https://...') compiles and is assignable to AccessDenialService. This ensures the interface is sound before any real implementation is built.
If the GoRouter redirect callback evaluates the no-access route itself as a blocked destination, it will trigger an infinite redirect loop, crashing the navigator.
Mitigation & Contingency
Mitigation: Add an explicit guard condition in the redirect callback: return null (no redirect) when the current location is already the no-access route or the logout route. Write a dedicated unit test covering this exact scenario.
Contingency: If the redirect loop is detected in production, deploy a hotfix that adds the null-return guard; the feature can be toggled off via the existing feature-flag infrastructure while the fix is prepared.
The access-denial-service may read role state before authentication completes (e.g. during app resume), causing a temporary false-positive block that redirects valid peer-mentor users to the no-access screen.
Mitigation & Contingency
Mitigation: Subscribe to the role-state-manager's loading/ready lifecycle and only evaluate role-based access once the RBAC state is confirmed as loaded. Return a 'pending' state that causes the guard to defer rather than redirect.
Contingency: Add a retry mechanism: if a user lands on the no-access screen but their role subsequently resolves as non-blocked, automatically navigate them to the role-based home screen.