critical priority high complexity testing pending testing specialist Tier 8

Acceptance Criteria

Integration tests cover the Vipps PKCE flow end-to-end: PKCE pair generation → authorization URL construction → mock callback handling → token exchange → personnummer extraction → Supabase identity link.
Integration tests cover the BankID flow: session initiation → polling → assertion retrieval → assertion validation → Supabase identity link → session creation.
Biometric enrollment integration test: first-time login completion → biometric capability check → enrollment prompt → confirmation → Secure Storage write verified.
Biometric session resumption integration test: biometric challenge mocked as success → refresh token retrieved → session resumed → Authenticated state emitted → new token written.
JWT auto-refresh test: access token approaching expiry (mocked) → Auth Session Manager triggers refresh → new token pair stored → no interruption to auth state stream.
Auth state stream transitions are tested for all flows: initial Unauthenticated → Loading → Authenticated → SessionExpired → Unauthenticated covers the full lifecycle.
Network failure tests: HTTP timeout during Vipps token exchange → NetworkFailure error state emitted; Supabase unreachable during BankID link → BankIdLinkingFailure emitted with retry option.
User cancellation tests: user cancels Vipps browser redirect → UserCancelled state emitted, no partial session; biometric prompt cancelled → BiometricChallengeFailed, no token access.
Expired session test: refresh token rejected by Supabase → session cleared from Secure Storage → auth state stream emits Unauthenticated → UI navigated to login screen.
All tests are deterministic and pass consistently in CI (no flakiness from real network or real biometric hardware); all external services are mocked/stubbed.
Test coverage for the Auth Session Manager (024) reaches at least 85% line coverage.

Technical Requirements

frameworks
Flutter
flutter_test
BLoC test (bloc_test)
Riverpod
Dart
mocktail or mockito
apis
Supabase Auth (mocked via test double)
Vipps OIDC token endpoint (mocked)
BankID assertion endpoint (mocked)
local_auth (mocked via IBiometricAuthService interface)
flutter_secure_storage (mocked via in-memory map)
data models
AuthState (all variants: Unauthenticated, Loading, Authenticated, SessionExpired, Error)
VippsTokenResponse (accessToken, idToken, personnummer)
BankIdAssertion (assertion, pid, identityLevel)
SupabaseSession (accessToken, refreshToken, expiresAt)
BiometricCapability
BiometricSessionResult
performance requirements
All integration tests must complete within 30 seconds total in CI
No real network calls — all HTTP intercepted by mock HTTP client
No real biometric hardware calls — all local_auth interactions mocked
security requirements
Test fixtures must not contain real personnummer, real tokens, or production credentials
Mock tokens used in tests must follow JWT structure but contain test-only claims (sub: 'test-user-id')
Secure Storage mock must be reset between tests to prevent state bleed

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Implementation Notes

Create a `TestAuthFixtures` class with reusable mock data: test JWT tokens, mock personnummer hash, mock BiometricCapability objects. Use `mocktail` (preferred over mockito for null-safety ergonomics) to stub Supabase client, local_auth, and flutter_secure_storage. Structure the test file hierarchy to mirror the service layer: `test/auth/vipps_auth_service_test.dart`, `test/auth/bankid_auth_service_test.dart`, etc. For BLoC tests, use `bloc_test`'s `blocTest()` helper with `build`, `act`, `expect` pattern.

For stream tests, use a `StreamController` to simulate auth state changes and verify downstream behavior. Critical: biometric session resumption tests must verify that the refresh token is NOT read if the biometric challenge returns false — this is a security invariant, not just a functional one. Add a CI step in the pipeline that runs `flutter test --coverage` and uploads coverage to a reporting tool.

Testing Requirements

Use flutter_test for unit/integration layer tests and bloc_test for BLoC state sequence assertions. Organize tests into test groups: (1) VippsAuthService, (2) BankIdAuthService, (3) BiometricAuthService (enrollment), (4) BiometricAuthService (session resumption), (5) AuthSessionManager (JWT lifecycle), (6) Cross-service error paths. Use `setUp`/`tearDown` to reset mock Secure Storage and mock Supabase client. For each service, cover: (a) happy path full flow, (b) network failure mid-flow, (c) user cancellation, (d) invalid/expired token, (e) concurrent call safety (no race condition on token refresh).

Use `expectLater(stream, emitsInOrder([...]))` for auth state stream assertions. Run with `flutter test --coverage` and enforce 85% minimum via CI threshold.

Component
Authentication Session Manager
service medium
Epic Risks (4)
high impact medium prob technical

The PKCE OAuth flow requires the code verifier to survive an app backgrounding during the Vipps redirect, which can trigger OS memory pressure and clear in-memory state. If the verifier is lost between authorization request and callback, the token exchange fails and the user is stranded with a confusing error.

Mitigation & Contingency

Mitigation: Store the PKCE code verifier in AuthTokenStore (Flutter Secure Storage) immediately after generation, before launching the Vipps redirect. Clear it only after a successful or explicitly failed token exchange.

Contingency: If state loss occurs in production, implement a retry flow that generates a new PKCE pair and restarts the authorization URL request, with a user-visible 'Try again' prompt rather than a generic error.

medium impact medium prob technical

Resuming a Supabase session after biometric verification requires the session token to still be valid. If the session has expired in the background (e.g., after a long device offline period), biometric success will not produce a valid session, and the user will see a confusing 'Face ID worked but still logged out' experience.

Mitigation & Contingency

Mitigation: Before presenting the biometric prompt, check session token expiry. If expired, skip biometrics and route directly to full BankID/Vipps re-authentication. Only offer biometric re-auth if the stored refresh token is still within its validity window.

Contingency: If session expiry during biometric flow occurs in production, implement a graceful transition message ('Your session has expired — please log in again') that preserves the user's last-used authentication method preference.

high impact medium prob integration

BankID and Vipps may return different user identifiers (personnummer, phone number, sub claim) that must be correctly linked to an existing Supabase auth user. If the linking logic has edge cases (e.g., user previously registered via email/password), duplicate Supabase accounts may be created.

Mitigation & Contingency

Mitigation: Design the identity linking logic with explicit disambiguation: check for existing users by personnummer before creating a new Supabase identity. Implement the linking via Supabase Edge Function to keep the logic server-side and auditable.

Contingency: Implement an admin-facing account merge tool in the admin portal to resolve duplicate accounts if they occur. Add a Supabase unique constraint on the personnummer field to make duplicates fail loudly rather than silently.

medium impact high prob dependency

The Vipps nin (personnummer) scope requires explicit approval from Vipps as part of the merchant agreement. If this scope approval is not in place before the production release, the Vipps flow will succeed but return no personnummer, making the primary business value (membership data gap fill) non-functional without user-visible error.

Mitigation & Contingency

Mitigation: Apply for Vipps nin scope approval as part of the merchant onboarding process, well before Phase 2 launch. Implement the service to gracefully handle absent nin claims and show users a clear message if personnummer could not be retrieved.

Contingency: If nin scope is delayed, ship the Vipps login flow without personnummer write-back first (delivering login value immediately) and add personnummer sync as a post-approval update with no UI changes required.