Build field_encryption_utils org key retrieval
epic-contact-detail-and-edit-foundation-task-007 — Implement field-encryption-utils with org-specific encryption key retrieval from Supabase Vault or equivalent secure storage. Key lookup must be scoped to the authenticated user's organisation and cached in memory for the session lifetime. Include key rotation awareness — if key version changes, re-fetch before decryption.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
Use Dart's `pointycastle` package for AES-256-GCM; it is pure Dart and does not require platform channels. Implement the cache as a `final Map
Never log the decoded bytes; only log that a key was fetched for '{orgId}:{keyVersion}' at debug level. Implement a KeyVersion value type to prevent orgId/keyVersion argument order mix-ups.
Testing Requirements
Unit tests with a mocked Supabase RPC client: (1) first call fetches from RPC and caches; (2) second call with same orgId+keyVersion returns cached value without RPC call; (3) call with new keyVersion invalidates cache and re-fetches; (4) RPC error throws KeyRetrievalException; (5) decryptField produces expected plaintext from known ciphertext+key test vector; (6) encryptField produces ciphertext that decryptField can reverse; (7) signing out clears the cache (verify RPC is called again on next getOrgKey after sign-out). Security test: assert that the Uint8List is zeroed (all bytes == 0) after cache clear. Integration test against a real Supabase Vault instance (in a sandboxed test project): verify cross-org key access is blocked. flutter_test is sufficient for all unit tests; no widget test required.
Blindeforbundet's encryption key retrieval mechanism may not be finalised at implementation time, or session key availability via Supabase RLS may be inconsistent, causing decryption failures that expose masked placeholders to users and degrade the experience.
Mitigation & Contingency
Mitigation: Agree with Blindeforbundet on key storage and retrieval contract before implementation starts. Prototype key retrieval in a spike against the staging Supabase instance and validate the full decrypt/verify cycle with real test data before committing to the implementation.
Contingency: Implement a fallback that shows a 'field temporarily unavailable' state with a retry affordance. Log decryption failures server-side for audit. Escalate to Blindeforbundet stakeholders to unblock key management before the service tier epic begins.
NHF contacts may belong to up to 5 chapters, each governed by separate RLS policies. A coordinator's chapter scope may not cover all affiliations, causing partial profile reads or silent data omissions that are difficult to detect in tests.
Mitigation & Contingency
Mitigation: Map all RLS policy combinations for multi-chapter contacts early. Write integration tests that create contacts with 5 affiliations and query them from coordinators with varying chapter scopes. Use Supabase's RLS test utilities to verify row visibility per role.
Contingency: Add an explicit 'affiliation partially visible' state in the repository response model so the UI can communicate scope limitations to the coordinator rather than silently showing incomplete data.
Organisation-specific validation rules (e.g., NHF chapter limit, Blindeforbundet encrypted field edit flow) may expand in scope during implementation as edge cases are discovered, causing the validator to grow beyond the planned complexity.
Mitigation & Contingency
Mitigation: Define the complete validation rule set with product and org stakeholders before coding begins. Document each rule with its source organisation and acceptance test. Use a rule registry pattern so new rules can be added without modifying core validator logic.
Contingency: Timebox validator enhancements to 2 hours per additional rule. Defer non-blocking rules to a follow-on maintenance task rather than blocking the epic delivery.