necessary library

Device integrity for Flutter apps: the client half of the public integration guide. A device-bound key the plugin never exports, signed measurements of the phone and the app, and request proofs; the app's backend decides what they mean.

Classes

AppPinOptions
Where the backend takes the app PIN's two calls (the public guide, section 4).
DeviceTrust
DeviceTrustClient
DeviceTrustTransport
Sends one request to your backend and answers its JSON object. A refusal is thrown as an ApiException with the backend's code, message and details (the public guide, section 4, Errors).
HttpTransport
JSON over HTTPS under baseUrl. A release build refuses any other scheme, so a mistyped endpoint fails loudly instead of sending tokens in the clear.
InstallationIdentity
InstallationStore
NativeInstallationKey
NativeIntegrityCollector
NativeKeyMetadata
NativeStepUpKey
ReinstallHint
ReinstallHintReader
RequestProof
What the installation key signed for one request -- with step-up, what the step-up key signed; with the app PIN, the backend's proof of it; for a request a scan approves, that scan's report -- and the headers that carry them beside Authorization: Bearer.
ScanChallenge
A challenge for a scan, as your backend sent it.
ScanResult
One scan: the challenge it answered, the report as signed, and the backend's answer, whole.
SecureScreen
Protects the app's window while a secret is typed. Android: no screenshots, recordings or casts of the window, other apps' windows not drawn over it (Android 12 and later), and touches that passed through another app's window dropped. iOS cannot block screenshots; it covers the window while the screen is recorded or mirrored.
SignedReport
A report as the installation key signed it.
StepUpApproval
The person's approval of one request: the step-up key's factor, and sign, which has the step-up key sign a payload -- the phone asks the person (NativeStepUpKey.sign).
StepUpKeyMetadata
The optional step-up key as the native side reports it. factor, mode and windowSeconds describe what the platform keystore actually enforces (read back from KeyInfo on Android, from the key's label on iOS), which on Android 9/10 is a short hardware window rather than per-use.
StepUpOptions
How the app wants its step-up key made (the public guide, section 3): factor passcode or biometric; mode per_use, or windowed with windowSeconds from 1. Android 9 and 10 cannot make a per-use passcode key and make a windowed one; iOS makes per-use keys only. reenrolPath is where the backend takes a new key (DeviceTrust.reenrolStepUp).

Enums

PersonalMeasurement
The personal measurements an app may enable (the public guide, section 8). Each is collected only when the backend asks for it as well -- the location only with the person's permission too.
ProtocolStep
What the client does about a refusal from your backend's API: the protocol errors of the integration guide's section 4, each acted on once. Any other refusal goes to the app as it came.

Constants

protocolMaxWaitSeconds → const int
The longest wait the client sits through before it retries a 429 itself; a longer one goes to the app.
reportByteLimit → const int
The most a backend takes: a report of 65,536 bytes of JSON (the measurement contract, section 1).
shortenableLists → const Map<String, List<String>>
The lists a report may shorten, by measurement -- each beside the truncated flag that says the phone had more (the measurement contract, section 6).
stepUpReenrolPath → const String
Where a backend takes a re-enrolled step-up key by default (the public guide, section 4).

Functions

appPinProofRequest({required String pin, required String method, required String path, required String encodedBody, required String nonce}) → Map<String, dynamic>
What the app sends to have its backend check the app PIN for one request: the PIN, and that request as its request proof signs it -- method, path, query, the hash of the body sent and the request proof's nonce.
base64UrlNoPadding(List<int> bytes) → String
Base64url without padding: how every binary value travels.
collectReport({required ScanChallenge challenge, required InstallationIdentity identity, required NativeIntegrityCollector collector, required List<String> enabledMeasurements}) → Future<SignedReport>
Measures what challenge asks for -- and, of the personal measurements it asks for, those in enabledMeasurements -- and signs the report with identity's key.
describeOperation(String method, String path, String encodedBody) → Map<String, dynamic>
The one request a scan is to approve, described exactly as it will be sent: what the challenge names as its operation.
fitReport(Map<String, dynamic> report) → Map<String, dynamic>
report made to fit reportByteLimit: the longest list it may shorten is halved, and marked truncated, until it fits. A report that cannot fit is never cut anywhere else: the answer is an error naming the limit.
freshNonce() → List<int>
32 random bytes from the platform's secure source.
protocolStepFor(ApiException error, {required bool deviceSession}) → ProtocolStep
deviceSession: the request ran under the session from /v1/installations/verify, which the client can renew by proving its key again; an account session is the app's to renew.
protocolWait(ApiException error) → Duration?
How long a 429 asks the client to wait, when that is short enough to wait.
replacesStepUpKey(StepUpKeyMetadata key, StepUpOptions options, {required bool replaceMismatched, required bool knownInstallation}) → bool
Whether a step-up key options do not accept is replaced before the next registration: only where the new key can then be bound -- by a re-enrolment (replaceMismatched), which binds it with the person's re-authentication, or by this installation's first registration, which binds the key it is offered. A known installation's registration never binds an offered key; it only says whether it matches, so replacing the key there orphans the one the server holds: on a phone that cannot make the key the options ask for (Android 9 makes no per-use passcode key), the registration that follows every re-enrolment would replace the key the server had just bound, and step-up would stay unavailable.
signRequest({required InstallationIdentity identity, required String method, required String path, required String bearerToken, String encodedBody = '', StepUpApproval? stepUp, String? installationId, int? timestamp, List<int>? nonce}) → Future<RequestProof>
Signs method path with encodedBody under bearerToken: proof version 2, which signs the query string too, exactly as sent. A GET signs an empty body. With stepUp, the step-up key signs first -- the phone asks the person -- and the request proof after, with the same nonce, so the time the person takes does not count against the request proof's clock window. installationId, timestamp and nonce replace what the request proof would otherwise carry, for the shared test vectors and for testing a backend's refusals.
stepUpProof({required String installationId, required String accessToken, required String method, required String path, required String encodedBody, required String factor, required String nonce, required int timestamp}) → String
The step-up proof, version 2, that the step-up key signs for one request: the base64url of its JSON, the members in this order. nonce is the request proof's nonce for the same request, so the approval is used up with it.
stepUpReenrolProof({required String installationId, required String stepUpKeyThumbprint, required String nonce, required int timestamp}) → String
The proof a new step-up key signs at re-enrolment: the base64url of its JSON, the members in this order. nonce is the request proof's nonce of the re-enrolment request.

Exceptions / Errors

ApiException
A refusal from your backend, or a request that did not reach it: the backend's {"error": {"code", "message", "details"}} (the guide, section 4), with the HTTP status.
InterruptedCallException
A call that DeviceTrust.reset or DeviceTrust.close ended while it ran: what it found belongs to an installation that is gone, or to a client that is closed, so nothing of it was kept. After a reset, call again.
NativeKeyException
A failure of the phone's keystore or of a prompt: code says which -- KEY_UNAVAILABLE (Android's keystore could not be reached for the installation key; no key was made over it: try again, or reset()), STEPUP_KEY_INVALIDATED (the key can never sign again: re-enrol), STEPUP_KEY_UNAVAILABLE (the keystore could not say whether the key is usable, or could not be reached; it was kept: try again, or re-enrol), STEPUP_CANCELLED, STEPUP_REENROL_PENDING and the like -- and message says it in words.