Implement SecureStorageAdapter with platform-specific encryption options
epic-bankid-vipps-login-foundation-task-002 — Provide the concrete implementation of the SecureStorageAdapter interface using flutter_secure_storage. Configure IOSOptions (accessibility: first_unlock_this_device, synchronizable: false) and AndroidOptions (encryptedSharedPreferences: true). Add unit tests mocking the underlying plugin to verify read/write/delete/clear behavior and error propagation.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Inject FlutterSecureStorage as a constructor parameter (not created internally) so it can be mocked in tests: FlutterSecureStorageAdapter({FlutterSecureStorage? storage}) with storage ??= const FlutterSecureStorage(). Create the _iosOptions and _androidOptions as final private fields in the constructor body. Use a try/catch around every flutter_secure_storage call — PlatformExceptions can occur due to Keystore corruption, device policy restrictions, or the user uninstalling and reinstalling the app (Android backup issues).
Log errors at the error level via the app logger before rethrowing as StorageException. The Riverpod provider registration belongs in task-001's providers file — this task only implements the class.
Testing Requirements
Unit tests using flutter_test and Mockito or mocktail to mock FlutterSecureStorage. Test file at test/core/storage/flutter_secure_storage_adapter_test.dart. Required test cases: (1) write + read round-trip returns correct value; (2) write + delete + read returns null; (3) clear + read on any key returns null; (4) PlatformException during read is caught and StorageException is thrown; (5) PlatformException during write is caught and StorageException is thrown. Do not test IOSOptions/AndroidOptions values directly — they are private implementation details.
Aim for 100% line coverage of FlutterSecureStorageAdapter.
Flutter Secure Storage behavior differs between iOS Keychain and Android Keystore — key accessibility attributes (kSecAttrAccessibleWhenUnlocked vs. WhenUnlockedThisDeviceOnly) may cause tokens to become inaccessible after device restart or OS upgrade, breaking session restoration for returning users.
Mitigation & Contingency
Mitigation: Define explicit Keychain accessibility attributes during implementation and write integration tests on both platforms. Follow flutter_secure_storage documentation for cross-platform accessibility configuration.
Contingency: Implement a recovery flow that detects secure storage read failures and falls back to full re-authentication rather than crashing. Add a migration utility to re-write tokens with corrected attributes if a misconfiguration is discovered post-release.
Personnummer is a legally sensitive national identifier under Norwegian GDPR implementation. If encryption-at-rest or data minimization requirements are not met before launch, the feature could be blocked by legal/compliance review from any of the four partner organizations.
Mitigation & Contingency
Mitigation: Ensure personnummer is only persisted after explicit user consent via the personnummer confirmation widget. Use Supabase column-level encryption for the personnummer field. Document the data processing basis and retention policy before the first TestFlight release.
Contingency: If legal review blocks the personnummer write-back, implement the feature as opt-in only with a deferred sync model, allowing BankID/Vipps login to proceed without storing the personnummer until compliance is confirmed.
If the VippsOrgCostConfig data is not populated in Supabase for all four partner organizations before the feature ships, users from unconfigured organizations will see no Vipps login option and may report it as broken, creating confusion and support load.
Mitigation & Contingency
Mitigation: Create a seed migration script for Vipps org configuration and include it in the deployment checklist. Implement a clear admin UI warning when an organization is missing Vipps configuration.
Contingency: Add a feature flag in VippsOrgCostConfig so individual organizations can be enabled/disabled without a code deploy, allowing rapid remediation.