critical priority medium complexity api pending api specialist Tier 1

Acceptance Criteria

exchangeCodeForTokens(code, codeVerifier, redirectUri) posts to /v2/login/token with correct content-type (application/x-www-form-urlencoded) and PKCE code_verifier, returning a typed VippsTokenResponse
fetchUserinfo(accessToken) calls GET /v2/login/userinfo with Bearer auth header and returns a typed VippsUserinfoResponse containing sub, phone_number, and nin (personnummer)
refreshAccessToken(refreshToken) calls the token endpoint with grant_type=refresh_token and returns a new VippsTokenResponse
All three operations return Either<VippsAuthFailure, T> (or equivalent typed result); no raw exceptions escape the client boundary
HTTP 400 (invalid_grant), 401 (unauthorized), 5xx (server error), and network timeout are each mapped to distinct VippsAuthFailure subtypes defined in task-001
personnummer (nin) is never logged, stored in SharedPreferences, or included in crash reports; only passed to the auth service layer
Client is registered as a Riverpod provider accepting VippsEndpointConfig from task-002
Client sets a User-Agent header identifying the app and version per Vipps API requirements
Token response includes expiry timestamp computed from expires_in field at reception time (not stored as raw seconds)
PKCE code_verifier is accepted as a parameter and never generated or stored inside the client itself

Technical Requirements

frameworks
Flutter
Riverpod
Dart
http or dio (Dart HTTP client)
apis
Vipps Login API /v2/login/token
Vipps Login API /v2/login/userinfo
performance requirements
Token exchange must complete within 10 seconds network timeout
Userinfo fetch must complete within 8 seconds network timeout
No retry logic inside the client — retries are the caller's responsibility
security requirements
PKCE code_verifier transmitted over TLS only — never logged
personnummer (nin) from userinfo response must not be logged or cached beyond the immediate auth flow
Access tokens stored in flutter_secure_storage (iOS Keychain / Android Keystore) by the service layer, not by this client
Phone number and NIN encrypted at rest per GDPR requirements; client passes raw value to service, service handles storage
No Vipps session tokens persisted beyond authentication — client returns them to caller immediately
PKCE required to prevent authorization code theft on mobile deep links

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Use Dart's http package (or dio) with a custom HttpClient abstracted behind an interface for testability. Model all requests as value objects: VippsTokenRequest, VippsUserinfoRequest. Parse responses into typed Dart classes with fromJson factories — never access raw map keys outside the model. Compute expiresAt = DateTime.now().add(Duration(seconds: expiresIn)) at parse time.

The refresh token flow should check if the refresh token itself is expired before making the network call. PKCE code_verifier must be received from the caller (generated by the deep link/OAuth layer) — the client is stateless. Register as Provider taking VippsEndpointConfig as a dependency. Consider wrapping the HTTP client in a thin interceptor that strips sensitive headers from debug logs.

Testing Requirements

Unit tests using flutter_test with a mock HTTP client (MockClient or dio interceptor). Test cases: (1) successful token exchange returns correct VippsTokenResponse fields; (2) successful userinfo returns typed response with sub and nin; (3) HTTP 400 maps to InvalidGrantFailure; (4) HTTP 401 maps to UnauthorizedFailure; (5) network timeout maps to NetworkFailure; (6) HTTP 500 maps to ServerFailure; (7) missing nin in userinfo response maps to MissingPersonnummerFailure. Assert no raw exceptions escape. Assert User-Agent header is present in all requests.

Component
Vipps API Client
infrastructure medium
Epic Risks (3)
high impact high prob dependency

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.

high impact medium prob technical

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.

medium impact medium prob dependency

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.