Implement Deep Link / OAuth Redirect Handler with state validation
epic-bankid-vipps-login-api-clients-task-006 — Build the Deep Link and OAuth Redirect Handler that intercepts incoming OAuth callback deep links for both Vipps and BankID providers. Implement OAuth state parameter generation (before redirect) and validation (on callback) to prevent CSRF attacks. Parse the authorization code and error parameters from the redirect URI, determine which provider triggered the callback, and route the result to the appropriate authentication service. Register the app's custom URI scheme and/or Universal Link for both providers.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Use the app_links package (preferred over uni_links for null safety and maintenance) to subscribe to incoming deep links as a Stream
The handler should be initialized early in the app lifecycle (in main() or a top-level provider) to avoid missing deep links that arrive while the app is cold-starting. iOS Info.plist CFBundleURLSchemes and Android intent-filter must both be configured. Test on physical devices — deep link behavior on simulators can differ.
Testing Requirements
Unit tests using flutter_test. Test cases: (1) generateOAuthState returns a 64-character hex string; (2) generatePkceChallenge returns correct S256 challenge for a known verifier; (3) valid state match routes to correct provider; (4) state mismatch returns StateMismatchFailure; (5) Vipps callback URI parses code and state correctly; (6) BankID callback URI parses correctly; (7) error=access_denied returns OAuthUserDeniedFailure; (8) expired state (TTL exceeded) returns StateExpiredFailure; (9) state and codeVerifier are deleted from storage after validation regardless of outcome. Mock flutter_secure_storage in tests.
Norway has multiple BankID broker providers (e.g., Signicat, Criipto, Nets) with different integration contracts, pricing, and WebView behavior. If the broker is not selected and contractually agreed before implementation begins, the BankIDProviderClient may need to be rewritten after initial build.
Mitigation & Contingency
Mitigation: Define a minimal broker interface abstraction (session initiation, WebView URL generation, assertion validation) before writing any provider-specific code. Confirm broker selection with Norse Digital Products before starting this epic.
Contingency: If the broker changes after implementation, the abstraction layer allows replacing the provider-specific implementation behind the same interface with a targeted rewrite rather than a full redesign.
Android deep link handling with custom URI schemes can conflict with existing app links (HTTPS-based) or fail silently on certain Android versions if the intent filter is misconfigured, causing OAuth callbacks to never reach the app and leaving users stranded on the Vipps or BankID page.
Mitigation & Contingency
Mitigation: Use HTTPS app links (Android App Links) rather than custom URI schemes where possible, as they are more reliable on modern Android. Test deep link receipt on Android 12+ explicitly during development, as this version changed intent flag requirements.
Contingency: Implement a polling fallback for Vipps (check auth status on app foreground) as a secondary callback mechanism if deep link receipt fails on specific Android configurations.
Vipps Login has a separate test environment (mt2.vipps.no) that requires distinct test merchant credentials which must be applied for separately. If test credentials are delayed, integration testing of the VippsApiClient cannot proceed, blocking the entire authentication flow.
Mitigation & Contingency
Mitigation: Apply for Vipps test merchant credentials at the start of the project sprint, not when implementation begins. Use Vipps' publicly documented mock token responses for unit tests to decouple unit testing from live credentials.
Contingency: Implement the VippsApiClient with full mock injection support so all service-layer tests can run against a stub client while waiting for official test credentials.