Attest¶
Register this device's signing key with DEA. The SDK creates the key the first time it is needed, inside the Android Keystore or the iOS Secure Enclave, where the private half cannot be read by your app or by the SDK. With the key, the SDK sends a Google Play Integrity token (Android) or an Apple App Attest token (iOS), so DEA can check that the device and the app are genuine before it accepts the key.
When to use it¶
- Before a sensitive step, to make sure the device is registered
- To show attestation status in a diagnostics screen
Prerequisites¶
- The SDK is installed and initialized. See the Quick Start.
- The values DEA issued for this platform's app are passed to
DeaSdk.init:appIdandappKeyon both platforms, andplayIntegrityProjecton Android. See Options. - The platform setup below.
Platform setup¶
Android: Play Integrity¶
Pass the Google Cloud project number DEA issued for your Android app as playIntegrityProject. It
must be the project your app is linked to on the App integrity page of Play Console. With any other
number, attest() answers failed.
DEA's service reads the token before it accepts the key. It requires that Google Play recognizes the app and that this copy was installed through Google Play, and that the device meets Play's device-integrity check. A build installed from a file, or signed with a key that is not registered in your app's Play Console entry, therefore fails attestation. Test with a build from a Google Play testing track, on a real device with Google Play services.
DeaSdk.init(
const DeaOptions(appId: 'YOUR_APP_ID'),
appKey: 'YOUR_APP_KEY',
playIntegrityProject: 123456789012, // read on Android only
);
iOS: App Attest¶
Add the App Attest capability to your app:
- Open
ios/Runner.xcworkspacein Xcode. - Select the Runner target, then Signing & Capabilities.
- Click + Capability and add App Attest.
This adds the com.apple.developer.devicecheck.appattest-environment entitlement with the value
development, Apple's App Attest sandbox, which builds you run from Xcode use. A build
distributed through TestFlight, the App Store or the Apple Developer Enterprise Program ignores the
value and uses Apple's production environment. Without the capability, App Attest refuses every
key: attest() answers failed, and attestation.detail typically mentions invalidKey.
App Attest needs iOS 14 or later on a real device. On the iOS Simulator, and on iOS 12 and 13, the
SDK sends the registration without an App Attest token, so platformAttested is false.
Usage¶
Attest¶
import 'package:dea_sdk/dea_sdk.dart';
final attestation = await DeaSdk.instance!.attest();
switch (attestation.verdict) {
case Verdict.passed:
// DEA accepted this device's key.
break;
case Verdict.unsupported:
// No key could be created on this device, or no appKey was given. See attestation.detail.
break;
case Verdict.failed:
// DEA refused, could not be reached, or the platform would not issue its token.
// See attestation.detail.
break;
}
attest()never throws.- A
passedresult is kept, on the device as well, until one minute before it expires, so callingattest()again costs nothing until then. Afailedorunsupportedresult is not kept: the next call tries again. - One exception: after DEA refuses a registration that carried a platform token, the SDK waits
before it asks Google or Apple for another — one hour, doubling with each further refusal up to
seven days. Until then
attest()answersfailedwithout contacting anyone, anddetailsays until when.
Check whether a platform token was sent¶
platformAttested says whether a Play Integrity or App Attest token went to DEA with the key:
if (attestation.verdict == Verdict.passed && attestation.platformAttested) {
// DEA accepted the key together with Google's or Apple's token.
}
It is false when the platform had no token to give: on the iOS Simulator, and on iOS 12 and 13.
When Google or Apple refuses to issue a token, attest() answers failed instead, with the
platform's reason in detail.
Read the last result without attesting again¶
attestation is whatever the last attest() answered in this run, including a refusal.
Check for an existing registration without contacting DEA¶
final stored = await DeaSdk.instance?.storedAttestation();
// null: attest() would have to register the device again
storedAttestation() reads the registration kept on the device, without any network request and
without asking Google or Apple for a token. It returns null when there is none or it is about to
expire.
Examples¶
Attest before a payment step¶
import 'package:dea_sdk/dea_sdk.dart';
import 'package:flutter/material.dart';
Future<void> openPayment(BuildContext context) async {
final attestation = await DeaSdk.instance?.attest();
if (attestation?.verdict != Verdict.passed) {
debugPrint('DEA attestation: $attestation, ${attestation?.detail}');
}
if (context.mounted) {
Navigator.of(context).pushNamed('/payment');
}
}
API¶
| Member | Returns | Description |
|---|---|---|
attest() |
Future<Attestation> |
Registers the device key and returns the result |
attestation |
Attestation? |
The last result in this run, without attesting again |
storedAttestation() |
Future<Attestation?> |
The registration kept on the device, if still fresh; no network |
forgetAttestation() |
Future<void> |
Forgets the registration, so the next attest() registers again |
Attestation:
| Field | Type | Description |
|---|---|---|
supported |
bool |
Whether a key could be created on this device. false also when no appKey was given |
verdict |
Verdict |
passed, failed or unsupported |
platformAttested |
bool |
Whether a Play Integrity or App Attest token went to DEA with the key |
expiresAt |
int? |
Expiry, in epoch seconds |
kid |
String? |
The key's identifier |
tier |
String? |
The trust tier DEA assigned |
backing |
String? |
Where the key is kept on the device: strongbox, tee, secure_enclave or software |
integrity |
String? |
DEA's reading of the device's integrity. DEA's service does not send one at present, so it is null |
detail |
String? |
Why attestation failed or is unsupported |