critical priority medium complexity integration pending integration specialist Tier 2

Acceptance Criteria

validateCurrentSession() returns SessionValidationResult.valid when Supabase Auth confirms the JWT is valid and the user record is active
validateCurrentSession() returns SessionValidationResult.expired when the JWT exp claim is in the past, without making a network call
validateCurrentSession() returns SessionValidationResult.revoked when Supabase Auth returns 401 or the user record is marked inactive/deleted
validateCurrentSession() returns SessionValidationResult.networkUnavailable when the device has no connectivity and the local token has not yet expired
Local expiry check (JWT exp claim parsing) is performed before any network call — avoids unnecessary API calls for clearly expired tokens
Network validation calls supabase.auth.getUser() which performs server-side JWT verification — not just client-side JWT decode
Validation result includes a validUntil DateTime field on SessionValidationResult.valid for upstream callers to schedule next validation
Method is idempotent and safe to call concurrently — multiple simultaneous calls return the same result without duplicate network requests
Validation is called on app cold start, app resume, and before any sensitive data write operation
All four result variants are handled by all callers — sealed class forces exhaustive switch

Technical Requirements

frameworks
Riverpod (for connectivity state check)
Dart async (Future, sealed classes)
apis
Supabase Auth SDK — supabase.auth.getUser() (server-side JWT validation)
Supabase Auth SDK — supabase.auth.currentSession (local session access)
connectivity_plus (or equivalent connectivity check)
performance requirements
Local expiry check (no network) must complete in under 5ms
Full network validation (supabase.auth.getUser) must complete within 3 seconds on typical mobile connection
Concurrent call deduplication must not add more than 1ms overhead per additional caller
security requirements
Server-side validation via getUser() is mandatory for sensitive operations — local JWT decode alone is insufficient
Revoked state must trigger immediate token clearing from SecureSessionStorage
networkUnavailable must not be treated as valid — callers must not allow access to sensitive data in this state without explicit offline-mode opt-in
JWT token value must not appear in exception messages or log output

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Implement the two-phase validation pattern: (1) Local phase — parse JWT exp claim from SecureSessionStorage, if expired return immediately without network call. (2) Network phase — call supabase.auth.getUser() which internally validates the JWT on Supabase's server. This pattern minimizes API calls while ensuring server-side revocation is detected. For deduplication, reuse the same Completer?

_pendingValidation pattern from the refresh logic. When returning networkUnavailable, do not clear tokens — the session may still be valid once connectivity returns. Use a ConnectionChecker abstraction (injected interface) rather than calling connectivity_plus directly, to keep the class testable. The validUntil field on the valid result should be the JWT exp claim DateTime minus the refresh window, so callers can schedule their next validation before the token expires.

Testing Requirements

Unit tests with mocked Supabase Auth client and mocked connectivity provider. Test scenarios: (1) Valid JWT, getUser() returns active user — expect SessionValidationResult.valid with correct validUntil. (2) JWT exp claim past — expect SessionValidationResult.expired without any network call (verify mock getUser never called). (3) getUser() returns 401 — expect SessionValidationResult.revoked and verify token clearing called on SecureSessionStorage mock.

(4) getUser() throws SocketException — expect SessionValidationResult.networkUnavailable. (5) Two concurrent calls — verify getUser() called exactly once (deduplication). Integration test: on a real Supabase test project, revoke a session server-side and verify the method correctly returns revoked within one validation cycle.

Component
Supabase Session Manager
infrastructure medium
Epic Risks (3)
high impact medium prob technical

Multiple concurrent callers (e.g., SessionResumeManager and a background sync service) could simultaneously detect a near-expired token and each invoke SupabaseSessionManager.refreshSession(), causing duplicate refresh API calls and potentially a token invalidation race condition on the Supabase Auth server. This can result in one caller receiving a valid refreshed token while another receives a 401, causing intermittent authentication failures.

Mitigation & Contingency

Mitigation: Implement a single-flight pattern inside SupabaseSessionManager so that concurrent refresh calls coalesce into one in-flight request. Use a Dart Completer or AsyncMemoizer to ensure all waiters receive the same refreshed token. Write a concurrent integration test to validate the single-flight behaviour.

Contingency: If the single-flight pattern introduces deadlocks or timeout complexity, fall back to a mutex-based lock with a 10-second timeout, logging a warning if the lock is held longer than expected, and triggering a full re-login if the refresh ultimately fails.

high impact low prob security

Supabase Row-Level Security policies evaluate the JWT claims (user_id, role, org_id) on every query. If the refreshed token contains stale or changed claims — for example if a coordinator's role was updated server-side — RLS may silently block data access even though the session appears valid from the client's perspective, causing confusing empty screens rather than an authentication error.

Mitigation & Contingency

Mitigation: After every token refresh, decode the new JWT and compare key claims (role, org_id) with the cached values. If claims have changed, emit a session-claims-changed event that triggers a role re-resolution and navigation reset. Document this behaviour in the SupabaseSessionManager API contract.

Contingency: If claims drift is detected in production and causes data visibility issues, provide a force-refresh mechanism in the UI (pull-to-refresh on home screen) that clears cached role state and re-fetches from Supabase, accompanied by a user-visible toast indicating the session was refreshed.

medium impact medium prob security

Allowing session resumption from cached local token when offline introduces a window where a revoked or invalidated session can still grant app access. For example, if a coordinator deactivates a peer mentor's account while the mentor is offline, the mentor continues to have access until connectivity is restored and the token is validated server-side.

Mitigation & Contingency

Mitigation: Set a maximum offline grace period (e.g., 24 hours) stored alongside the token in SecureSessionStorage. If the grace period is exceeded, force a full credential re-login regardless of connectivity status. Scope offline access to read-only operations only, requiring connectivity for any write that reaches Supabase.

Contingency: If the offline grace period logic is found to be insufficient for compliance, implement remote session invalidation via a lightweight push notification that clears SecureSessionStorage even when the app is backgrounded, using FCM with a data-only message.