PackageTrack
Sign in Get early access

approov_service_flutter_httpclient

Approov enabled HttpClient

3.5.8 2.5K downloads/mo #384 most downloaded on pub.dev approov/approov-service-flutter-httpclient

What this package is like to depend on

Last release 2 days ago

21 Aug 2026

Release timing varies

gaps range from 1 weeks to 5 months

Nearly every release is documented

notes for 10 of 10 stable releases

Nothing withdrawn

no release was ever pulled

1 years old

10 releases · first in 2025

6 releases in the last 12 months

see the full history below

Release timeline

10 releases · Mar 2025 to Aug 2026
2026
Release Pre-release

Releases

latest 10
  1. 3.5.8 21 Aug 2026
    Release notes

    Add SwiftPM support, empty-config bypass mode, and isInitialized/isAp…

    Open source →
    Release notes

    Upgrade notes:

    • await ApproovService.initialize(config) now completes only after the native initialization attempt finishes, and throws ApproovException on failure — previously it returned immediately and swallowed native failures. Wrap it in try/catch if you want to fall back to bypass mode (see README).
    • Every successful initialize()/re-initialize now resets the runtime configuration (custom mutator, header overrides, token binding, substitutions, exclusions, message signing, cached pinning certificates). Apply configuration after await initialize(...) returns; configuration applied before it is discarded.
    • Mixed-quickstart apps: the service layer no longer tolerates the native SDK reporting that it was already initialized with a different configuration. Previously that outcome was caught and ignored, so an app in which another Approov quickstart had already initialized the SDK with different parameters kept working; it now surfaces as an ApproovException from initialize() (TESTING_REQUIREMENTS.md §1, "Cross-Service-Layer Different Config Initialization" — a different config must not be silently accepted). Align the configuration strings, or use a reinit... comment.
    • setUseApproovStatusIfNoToken(true) no longer allows a request to proceed. It is now purely a backend-visibility control over the token header value. If you relied on it to keep requests flowing on NO_NETWORK / POOR_NETWORK, install a custom ApproovServiceMutator that overrides handleInterceptorFetchTokenResult for the statuses you actually want to allow — MITM_DETECTED should not be one of them.
    • NO_APPROOV_SERVICE no longer sends an empty or prefix-only token header. Backends that treated the mere presence of Approov-Token as evidence the layer ran must now either enable setUseApproovStatusIfNoToken(true) (which sends NO_APPROOV_SERVICE as the value) or stop relying on the empty header.
    • Message signing failures now proceed unsigned instead of aborting the request, except for a required body digest that cannot be generated and an unsupported/missing signing algorithm, which still abort (TESTING_REQUIREMENTS.md §5; matches approov-service-okhttp). Backends enforcing signatures remain the enforcement point.
    • Log every substitution that leaves the placeholder in the request. A skipped substitution sends the placeholder where a credential belongs and raises no exception, so the log is the only evidence the decision was taken (TESTING_REQUIREMENTS.md §3), and previously there was none: a custom mutator declining was silent, and the empty-secure-string case logged at debug, which is below the WARNING default and therefore invisible in the field. UNKNOWN_KEY and an empty secure string now log at warning; any other status can only be skipped because a custom mutator overrode a policy that would otherwise have failed the request, so it logs at error naming the status.
    • Secure-string substitution now fails closed on NO_APPROOV_SERVICE. Both the header and query paths previously skipped the substitution and forwarded the request with the placeholder still in the header, reporting no error, so the backend received the placeholder as the credential and the app never learned anything had gone wrong. The NO_APPROOV_SERVICE carve-out in TESTING_REQUIREMENTS.md §3 applies to the Approov token fetch, where proceeding without a token is a coherent degraded state that the backend can act on; there is no equivalent for a secret, because the only alternative to failing is sending a wrong secret rather than an absent one. Both paths now throw ApproovException, matching approov-service-okhttp, approov-service-urlsession and approov-service-nsurlsession. Apps that would rather proceed can override the substitution handlers on a custom mutator. Note the consequence: during an Approov outage, requests carrying a configured substitution header now fail instead of going out with the placeholder.
    • Fix mixed-case staged signing headers leaking a superseded value onto the wire. ApproovSigningContext lowercases its header keys, but the client staged them case-sensitively, so a custom factory that touched the same header under two spellings (addHeader('X-Foo', …) then setHeader('x-foo', …), in either order) staged two entries whose replay no longer reproduced the single entry the signature covered: the request went out with both the current and the superseded value while the signature covered only the current one, and the backend could never verify it. Staging is now keyed by the lowercased name, preserving the first casing seen for the wire header. Reachable only from a custom SignatureParametersFactory; no effect on the default factory. Covered by a wire-level test rather than a signing-context assertion, since the context was never the broken half.
    • setProceedOnNetworkFail() is now an obsolete no-op (matching approov-service-okhttp). The argument is ignored and no default handler reads it, so NO_NETWORK / POOR_NETWORK / MITM_DETECTED now fail closed on every path: the token fetch and header/query substitution all throw ApproovNetworkException. The flag was a single global switch across every network-related status, so it could not proceed on "no network" without also proceeding on MITM_DETECTED — continuing after the SDK detected interception, potentially before dynamic pins had been received. Express the policy per status instead by overriding ApproovServiceMutator.handleInterceptorFetchTokenResult (or the substitution handlers) and installing it with setServiceMutator. ApproovTokenFetchResult.proceedOnNetworkFail is deprecated and always false.
    • Fix setUseApproovStatusIfNoToken(true) acting as a fail-open escape hatch on network and MITM statuses. The default mutator returned true for NO_NETWORK / POOR_NETWORK / MITM_DETECTED whenever the flag was set, so enabling a backend-visibility feature silently allowed requests to continue after the SDK had reported detected interception. The default mutator now throws ApproovNetworkException for those three statuses unconditionally (TESTING_REQUIREMENTS.md §3, "Default Mutator Behavior": fail-closed for every status except SUCCESS and NO_APPROOV_SERVICE). The flag now only controls what is put in the token header, never whether a request proceeds. The status-fallback injection path is unchanged and remains reachable for a custom mutator that deliberately overrides handleInterceptorFetchTokenResult and returns true. This diverges from approov-service-okhttp, which still has the same escape hatch — that layer needs the matching fix.
    • Fix an empty or prefix-only Approov-Token header being sent on NO_APPROOV_SERVICE. With the status fallback disabled the layer emitted Approov-Token: (or Approov-Token: Bearer with a prefix configured). TESTING_REQUIREMENTS.md §2 ("Missing Artifacts Fallback") requires empty token and trace values to be omitted rather than sent as empty-valued or prefix-only headers, with status evidence provided by setUseApproovStatusIfNoToken(true) instead. The header is now omitted entirely when no token is available, and NO_APPROOV_SERVICE joins the status-fallback allowlist so the status name is injected only when that flag is on. The trace-ID header already omitted itself when empty and is unchanged. This diverges from approov-service-okhttp, whose buildTokenHeaderValue returns prefix + token and so still emits the empty/prefix-only header for this status — that layer needs the matching fix.
    • Fix NO_APPROOV_SERVICE turning an Approov outage into a hard request failure for apps using secure-string substitution. The default mutator continues on this status, so _updateRequest proceeded into the substitution loop, where NO_APPROOV_SERVICE fell through to the default: case and threw ApproovException — every request carrying a configured addSubstitutionHeader / addSubstitutionQueryParam placeholder failed whenever the Approov service was unavailable, where the previous release forwarded the request unmodified. Both substitution handlers now return false for this status, skipping the substitution and leaving the original placeholder in place, so the request is forwarded with only the available artifacts (TESTING_REQUIREMENTS.md §2). This diverges from approov-service-okhttp, whose substitution handlers still throw for this status — that layer needs the matching fix.
    • Fix in-flight requests being failed by a rejected re-initialization. initialize() published the in-flight attempt as the future every _requireInitialized() caller awaits before that attempt had been resolved, so an app protecting traffic with config A that called initialize(configB) — a config-refresh path, for example — failed every concurrent request with the native rejection of config B, even though the layer remained fully functional under config A. The attempt is now published only when Approov protection is not already active; while it is, _requireInitialized() keeps resolving against the still-valid initialization for the whole duration of the attempt (TESTING_REQUIREMENTS.md §1, "Different Config Failure State"). Two cases are deliberately unchanged and still gate traffic on the attempt: a first initialization, so a failed attempt stays observable and _requireInitialized() rethrows the original root-cause error rather than the generic "has not been initialized" message; and the "empty config → valid config" upgrade out of bypass mode, so requests issued during await initialize(realConfig) wait for it rather than going out unprotected (TESTING_REQUIREMENTS.md §1, "Empty Then Valid Configuration"). Bypass mode reports itself as initialized, so the distinction is protection being active, not the initialized flag.
    • Move the post-initialization state reset so it sits immediately before the state commit, with nothing that can throw in between. Previously the reset ran, then the platform method-call handler was installed, then _isInitialized / _initialConfig were committed — all inside a try that rethrows as ApproovException, so a failure in that window wiped the runtime configuration while _initialConfig still named the previous config (TESTING_REQUIREMENTS.md §1, "Service-Layer State Only Updated On Success").
    • Fix a misleading log line that reported "fallback token header injected" — printing null as the value — on any non-SUCCESS status that set a token header key. It now fires only when a fallback value was genuinely injected, and names the header it was written to.
    • Expose getInstallMessageSignature(message) publicly, completing the common service-layer interface (TESTING_REQUIREMENTS.md §7). The plumbing already existed privately and in both native plugins; only the public entry point was missing, so callers could not sign with the install key outside the automatic interceptor path. Returns base64 of the raw 64-byte ECDSA r||s form, converted from the platform SDK's DER output, which is what an RFC 9421 ecdsa-p256-sha256 verifier expects. Guarded like its siblings: ApproovException in bypass mode, or when the platform cannot produce a signature. Verified on device on Android and iOS: 64 bytes decoded, and distinct messages produce distinct signatures.
    • Report the service-layer version in the SDK user property. Initialization previously sent the bare approov-service-flutter-httpclient, so attestation records could not distinguish one release from another; it now sends approov-service-flutter-httpclient/<version>, matching how approov-service-okhttp reports. ApproovService.serviceLayerVersion exposes the value, and a unit test fails if it drifts from pubspec.yaml.
    • Fix message signing running after a token fetch that returned SUCCESS with no token. Both signing artifacts come from the token itself: install signing is verified against the public key it carries, and account signing uses its mskid claim. Signing headers produced without them cannot be verified by any backend, so the request now proceeds unsigned and logs why (TESTING_REQUIREMENTS.md §2 "Missing Artifacts Fallback"). The token header already required a non-empty token; the signing gate did not.
    • Fix the iOS certificate-collection session leaving non-server-trust authentication challenges uncompleted. The delegate returned without calling the completion handler, so URLSession waited for a disposition and the certificate fetch stalled until it timed out. Unhandled challenges now use NSURLSessionAuthChallengePerformDefaultHandling, the valid fallback (TESTING_REQUIREMENTS.md §4 "Authentication Challenge Dispositions Must Be Valid"). Pre-existing, unrelated to the SwiftPM work.
    • Fix an empty secure string overwriting the placeholder it was meant to replace. Both substitution paths only checked for null, so a defined-then-deleted secure string produced an empty or prefix-only header (Bearer on the wire) and rewrote a query parameter to key=, destroying the placeholder the backend needs to see. Both now require a non-empty value and leave the original in place otherwise, logging that the substitution was skipped — matching approov-service-retrofit and TESTING_REQUIREMENTS.md §2 "Missing Artifacts Fallback". Applies to the automatic paths and to substituteQueryParam().
    • Fix secure-string substitution reaching URLs the SDK does not protect (TESTING_REQUIREMENTS.md §2, "Unprotected Request Processing"). Two paths were affected: the default mutator returned true for UNPROTECTED_URL, which let header substitution run after the token fetch, and automatic query substitution ran before any token fetch could classify the URL, because dart:io fixes a request's URI at openUrl() time. Either could resolve a secure string into a request bound for a host Approov neither tokenizes nor pins. The default mutator now returns false for UNPROTECTED_URL (joining UNKNOWN_URL, and matching approov-service-okhttp), and query substitution now runs only when a pre-open classification positively confirms the URL is protected — any other outcome, including a network failure or an internal error, suppresses it. A custom mutator overriding handleInterceptorFetchTokenResult can opt header substitution back in, but not automatic query substitution: that is gated by the pre-open URL classification, which runs before any mutator is consulted and requires a confirmed SUCCESS. Apps that need a query parameter substituted regardless can call substituteQueryParam() explicitly.
    • Document that the config-taking constructors ApproovHttpClient([config]) and ApproovClient([config]) cannot report an initialization failure to the caller: a constructor cannot await, so initialize()'s new asynchronous throw is not catchable around construction. The failure is retained and rethrown from the first request through the client, and the pending error no longer surfaces as an unhandled asynchronous error. Prefer await ApproovService.initialize(config) before constructing the client, which is the pattern the README documents.
    • Add isInitialized() and isApproovEnabled() public API methods (Dart, Android, iOS).
    • Fix native initialization state being tracked per FlutterEngine rather than per process. The plugin instance is created once per engine, so an app with a second engine (background fetch, alarm or geolocation plugins) saw "not initialized" there while the process-wide Approov SDK was initialized and protecting traffic — and the Dart layer read that as "native unprotected" and committed real bypass mode, silently dropping token injection and pinning for every request from that engine. The state is now held in a process-wide static on both platforms (matching approov-service-okhttp), and isInitialized()/isApproovEnabled() are answered from it.
    • Query the native state over the background method channel when the foreground channel cannot answer, so isInitialized()/isApproovEnabled() and the empty-config protection probe fall back to a second route rather than treating one failed probe as an established state. This is defence in depth, not a fix for an observed defect: every other native call in the package uses the foreground channel unconditionally and works from background isolates. When neither channel answers, bypass is assumed (per TESTING_REQUIREMENTS.md §1) and now logged at error level rather than passed over silently.
    • Document the bypass-mode contract in REFERENCE.md: the guarded methods do not behave uniformly (throw / empty map / empty string / no-op / pass-through), and that difference is now stated per method.
    • Document that the comment argument participates in the native SDK's already-initialized matching, so a same-config re-initialization is accepted only with an identical comment or one starting with reinit (TESTING_REQUIREMENTS.md §1, "Comment Is Part Of The Platform SDK Initialization Identity").
    • Guard the iOS initialConfig argument against NSNull, as the comment and updateConfig arguments already are: -[NSNull length] is an unrecognised selector and would crash rather than read as empty.
    • Fix initialize('') (empty configuration string) to actually enter bypass mode — initializes the service layer without calling the native Approov SDK, instead of throwing. Previously this would fail with a native exception surfaced as a Dart PlatformException, contradicting documentation that claimed bypass-mode support.
    • Fix initialize() re-initialization guard to allow the "empty config → valid config" upgrade transition and to silently ignore a "valid config → empty config" downgrade attempt, per the cross-service-layer TESTING_REQUIREMENTS.md spec, instead of throwing in both directions.
    • Fix initialization to await the current native initialization attempt, preserve null comments when forwarding to native, forward every non-empty config to the native SDK, and preserve existing service-layer state when native initialization fails.
    • Reset runtime service-layer configuration after every successful initialization/re-initialization, including custom mutators, header overrides, token binding, substitutions, exclusions, message signing, and cached pinning certificates.
    • Fix bypass mode (empty configuration string) to genuinely behave as a plain, unprotected network client end-to-end, matching approov-service-react-native's pattern: the core request pipeline now skips token injection, pinning, and secure string substitution entirely for every request, and every other public method that talks to the native SDK directly (precheck, getDeviceID, fetchToken, getMessageSignature, getAccountMessageSignature, fetchSecureString, fetchCustomJWT, setDevKey, getPins, setDataHashInToken, substituteQueryParam) now fails cleanly with ApproovException("Approov is not enabled") (or a safe no-op/empty default, where that is the correct behavior) instead of crashing with a native exception. Previously only initialize('') itself worked; every other entry point still reached the uninitialized native SDK.
    • Fix a cross-thread race on iOS between the background-channel initialize write and the foreground-channel isInitialized/isApproovEnabled reads of the same state, matching the equivalent Android fix (volatile fields).
    • Fix automatic token binding to await setDataHashInToken(...) before fetching the bound Approov token.
    • Fix message signing fallback behavior so signing and serialization failures fail open, while required body-digest failures and unsupported algorithms still fail closed.
    • Fix Structured Fields date serialization conformance for syntactic min/max date values.
    • Fix a race where overlapping failed re-initialization attempts could leave a healthy, successfully initialized service throwing a stale initialization error from every API call: a failed attempt now restores a freshly resolved initialization future (never a captured earlier one) whenever a successful initialization is in effect.
    • Fix a state desynchronization window where a failure in the post-initialization telemetry call (setUserProperty) failed the whole initialize() after the native SDK had already committed — leaving Dart in bypass while native was protected. The Dart state now commits immediately after native success and the telemetry call is best-effort (matches approov-service-okhttp ordering).
    • Fix a cross-isolate divergence where a fresh isolate (or hot restart) calling initialize('') while the process-wide native layer was already protected would commit bypass mode locally — silently skipping pinning and token injection for that isolate's requests while isApproovEnabled() reported true. The Dart layer now queries native and adopts protected mode.
    • Fail-closed message signing classification now uses typed exceptions (RequiredBodyDigestException, UnsupportedSignatureAlgorithmException, both exported) instead of error-message string matching; a params object with a missing algorithm identifier now also fails closed, matching approov-service-okhttp. Custom SignatureParametersFactory implementations can throw RequiredBodyDigestException to force an abort.
    • Fix staged message signing header application to preserve multi-value header adds from custom factories (previously collapsed to the last value, producing signatures the server could never verify). Note: as in approov-service-okhttp, when signing fails open the request goes out without Content-Digest (signing-related headers are staged and only applied on success).
    • Log (rather than silently discard) the native SDK's already-initialized result on a same-config re-initialization, on both platforms.
    • Raise the logger dependency lower bound to ^2.1.0 (DateTimeFormat is used and was added in 2.1.0).
    • Deprecate prefetch() — it is now a no-op, matching the rest of the Approov service layer family (approov-service-retrofit, approov-service-urlsession, and others). The Approov SDK manages prefetching automatically; the explicit prefetch call is redundant.
    Open source →
  2. 3.5.6 13 Mar 2026
    Release notes

    Update CHANGELOG.md document

    Open source →
    Release notes
    • Add ApproovServiceMutator support across fetch APIs, request mutation flow, and pinning gate callbacks.
    • Add request mutation models: ApproovRequestMutations, ApproovRequestSnapshot, ApproovTokenFetchResult, and ApproovTokenFetchStatus.
    • Add setServiceMutator() / getServiceMutator() plus deprecated alias methods for naming parity.
    • Add service-layer logging controls: ApproovLogLevel with OFF, ERROR, WARNING, TRACE and setLoggingLevel() / getLoggingLevel().
    • Add detailed TRACE diagnostics for platform-channel method calls, timing, and failures (with sensitive-value redaction).
    • Add automatic query substitution APIs: addSubstitutionQueryParam() and removeSubstitutionQueryParam().
    • Add setUseApproovStatusIfNoToken(bool) and getUseApproovStatusIfNoToken() to control token-header status fallback behavior.
    • Add interceptor token-header fallback injection for allowlisted statuses when no token is available: NO_NETWORK, POOR_NETWORK, MITM_DETECTED.
    • Preserve mutator-first decision ordering: fallback injection only occurs when handleInterceptorFetchTokenResult(...) allows continuation.
    • Ensure configured token header name and prefix from setApproovHeader(...) apply equally to JWT and status fallback values.
    • Propagate Approov trace IDs to request headers.
    • Update USAGE.md and REFERENCE.md with status-fallback behavior, defaults, allowlist, and mutator interaction.
    • Restructure docs to OkHttp-style layout with README.md, USAGE.md, and REFERENCE.md.
    • (fix) Don't throw exception on missing public key
    Open source →
  3. 3.5.5 18 Dec 2025
    Release notes
    • Updates Approov IOS SDK to 3.5.3
    • Add a capability to retrieve an ARC(Attestation Response Code) via getLastARC()
    • Add a capability to retrieve pins from the Approov SDK via getPins().
    Open source →
  4. 3.5.4 05 Dec 2025
    Release notes
    • Ensure compatibility with Flutter 3.29+ threading model changes.
    Open source →
  5. 3.5.3 25 Nov 2025
    Release notes
    • Update Android SDK to version 3.5.3
    Open source →
  6. 3.5.1 21 Oct 2025
    Release notes
    • Update platform SDK to version 3.5.1
    Open source →
  7. 3.5.0 31 Jul 2025
    Release notes
    • Update platform SDK to version 3.5.0
    Open source →
  8. 3.4.2 20 May 2025
    Release notes
    • Async service initialize function now returns a future to enable awaits
    • Fix pub.dev listing to link to the correct github repo
    Open source →
  9. 3.4.1 09 May 2025
    Release notes
    • Support calling Approov from main isolate and any background isolate
    • Performance improvements
    • Allow reinitialization with the same configuration
    • Edge case bug fixes
    • Align major and minor version with native SDK
    Open source →
  10. 0.0.5 02 Mar 2025
    Release notes
    • Updated readme.
    • First published to pub.dev
    • Update iOS native pod package to 3.3.1
    Open source →

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive