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,messageanddetails(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
passcodeorbiometric; modeper_use, orwindowedwith 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
truncatedflag 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
challengeasks for -- and, of the personal measurements it asks for, those inenabledMeasurements-- and signs the report withidentity'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> -
reportmade to fit reportByteLimit: the longest list it may shorten is halved, and markedtruncated, 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
optionsdo 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
methodpathwithencodedBodyunderbearerToken: proof version 2, which signs the query string too, exactly as sent. A GET signs an empty body. WithstepUp, 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,timestampandnoncereplace 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.
nonceis 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.
nonceis 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_PENDINGand the like -- and message says it in words.