Define SecureStorageAdapter interface and Flutter Secure Storage integration
epic-bankid-vipps-login-foundation-task-001 — Create the typed SecureStorageAdapter abstraction layer wrapping flutter_secure_storage. Define the interface contract with read, write, delete, and clear operations using typed keys. Implement AES-256 encryption configuration for Android Keystore and iOS Secure Enclave. Establish the adapter as the single, injectable point of access for all encrypted local persistence.
Acceptance Criteria
Technical Requirements
Implementation Notes
Place all files under lib/core/storage/. Define SecureStorageKey as an enum with a toKey() method that returns the string key used by flutter_secure_storage (e.g., 'auth.access_token') — this prevents typos and makes refactoring safe. Register the Riverpod provider in a dedicated providers file (lib/core/storage/storage_providers.dart) to avoid circular imports. The concrete FlutterSecureStorageAdapter class should be defined in the same file as the provider registration.
Do not expose IOSOptions or AndroidOptions in the interface — configure them only inside the concrete class. Follow the Repository pattern: the adapter is infrastructure, not domain. Consider defining a StorageException typed error for read/write failures so callers can catch it specifically instead of platform exceptions.
Testing Requirements
Unit tests are not required for this task (interface-only). Integration test: verify that a FakeSecureStorageAdapter (in-memory Map implementation) satisfies the SecureStorageAdapter contract — write, read back, delete, then confirm read returns null, then clear and confirm all keys return null. This fake implementation should be committed alongside the interface for use in all downstream tests.
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.