Implement role-based post-login routing in AuthService
epic-email-password-login-auth-logic-task-005 — After successful authentication, AuthService resolves the authenticated user's role (admin, coordinator, peer_mentor) by querying the role repository. Return the role alongside the session result so the caller can route to the correct home screen. Handle the case where a user has no assigned role by returning a NoRoleResult that triggers the no-access screen.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
The Supabase query for role: `supabase.from('user_roles').select('role').eq('user_id', userId).maybeSingle()` — use maybeSingle() to get null instead of an exception when no row exists. Map the string role value ('admin', 'coordinator', 'peer_mentor') to the UserRole enum in the RoleRepository implementation, not in AuthService. Keep AuthService focused on orchestration. For GoRouter integration: define a redirect function in the router that reads sessionStateProvider; when AuthSuccess is emitted, navigate to the role-appropriate initial route.
This avoids hard-coding routing logic in the BLoC. Document the no-role scenario in a code comment: it applies to global admins who manage organizations but don't have an org-level role.
Testing Requirements
Unit tests with mocked RoleRepository and MockSupabaseClient: (1) signIn() with valid credentials + getRoleForUser returning coordinator → returns AuthSuccess with role: coordinator. (2) signIn() with valid credentials + getRoleForUser returning null → returns NoRoleResult. (3) signIn() with valid credentials + getRoleForUser throwing NetworkFailureException → returns AuthFailure(NetworkFailureException). (4) signIn() with invalid credentials → returns AuthFailure(InvalidCredentialsException) without calling getRoleForUser.
Widget test: NavigationRouter navigates to CoordinatorHome when role is coordinator, PeerMentorHome when peerMentor, NoAccessScreen when NoRoleResult.
Supabase GoTrue returns HTTP error codes and string messages that may change between SDK versions. Incorrect or incomplete mapping could cause the wrong user-facing message to be shown (e.g., showing a generic error instead of a specific credential error), violating the plain-language feedback acceptance criteria and potentially exposing security-sensitive information.
Mitigation & Contingency
Mitigation: Pin the supabase_flutter SDK to a specific minor version in pubspec.yaml. Write integration tests that mock the Supabase HTTP layer and assert each error code maps to the correct domain exception. Document the mapping table as a constant in AuthService.
Contingency: If an unrecognized error code is received at runtime, catch it as an UnknownAuthException and display a generic safe message. Alert via crash reporting for triage and SDK update.
If the user taps the sign-in button multiple times rapidly, concurrent authentication requests could result in race conditions: duplicate network calls, out-of-order state emissions, or multiple session tokens being written to secure storage.
Mitigation & Contingency
Mitigation: Use bloc concurrency transformer (droppable or restartable) to ensure only one authentication event is processed at a time. The BLoC should guard against submission while in LoginLoading state.
Contingency: Add a UI-level disable on the submit button when loading state is active as a secondary guard independent of BLoC concurrency control.