Secure API credential storage and rotation
epic-bufdir-report-export-api-integration-task-008 — Implement secure storage and rotation of Bufdir API credentials within the bufdir-api-client. Credentials must be stored in the integration credential vault (not in source code or plain config files), support rotation without service restart, and enforce org-level isolation so credentials for one organisation cannot be accessed by another. Provide a rotation procedure document.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Handles integration between different epics or system components. Requires coordination across multiple development streams.
Implementation Notes
Create the integration_credentials table via a Supabase migration using `vault.create_secret()` or store the encrypted payload in a jsonb column protected by pgsodium `vault.secrets`. The Edge Function pattern: `const cred = await supabase.from('integration_credentials').select('credential_payload').eq('org_id', orgId).eq('integration_type', 'bufdir').order('rotated_at', ascending: false).limit(1).single()` — then decrypt using `vault.decrypted_secrets` view or pgsodium `crypto_secretbox_open`. For the rotation procedure document, include a SQL snippet for inserting new credentials and a verification curl command against the Bufdir sandbox endpoint. Pin the Deno Edge Function runtime version in `deno.json` to prevent unexpected upgrades that could affect credential decryption behaviour.
The vault key management key (KMS key) must be documented in the ops runbook and stored outside Supabase.
Testing Requirements
Infrastructure/integration tests: (1) Edge Function with valid credentials for org_id returns success, (2) Edge Function with no credential row for org_id returns structured BufdirCredentialNotFoundError, (3) Edge Function with expired credential logs a warning and still attempts submission (credentials may still work after expiry window), (4) RLS test verifies a mobile client JWT cannot SELECT from integration_credentials. Security review checklist: grep codebase for any hardcoded credential strings, verify no credentials in Flutter asset bundle, verify Supabase Dashboard shows Vault encryption active. No flutter_test unit tests needed for this infrastructure task.
Norse Digital Products has not yet completed API negotiations with Bufdir. If negotiations stall or Bufdir's API design diverges significantly from expectations, the API client may need substantial rework, or the epic may be blocked indefinitely.
Mitigation & Contingency
Mitigation: Implement the client against a locally defined stub of the expected Bufdir API schema. Isolate all Bufdir-specific schema mapping in a single adapter class so that changes to the actual API schema require changes in only one place. Keep the epic in 'interface-ready' status until real API credentials are available for integration testing.
Contingency: If API negotiations are not completed within the planned window, defer this epic without impact on any other epic — the PDF/CSV fallback path from Epics 1–4 delivers full standalone value. Mark the epic as blocked and resurface when negotiations conclude.
Bufdir API credentials stored in the application or edge function environment could be exposed through misconfigured secrets management, log leakage, or a compromised deployment pipeline, allowing unauthorised Bufdir submissions on behalf of the organisation.
Mitigation & Contingency
Mitigation: Store all Bufdir API credentials exclusively in Supabase Vault (or the integration credential vault component), never in client-side code or environment variables accessible to the Flutter app. Transmit credentials only from within the edge function, not from the Flutter client. Implement credential rotation support from the outset.
Contingency: If a credential leak is detected, immediately revoke and rotate the affected API credentials through Bufdir's credential management portal, audit submission logs for any unauthorised calls, and notify Bufdir's technical contact per the API agreement's security incident clause.