Skip to content

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. Where the platform supports it, the registration also carries a Google Play Integrity (Android) or Apple App Attest (iOS) token, so DEA can check the device and the app are genuine.

DEA files the registration under the app your appKey belongs to. See Accounts and keys.

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 with your appId and appKey, and on Android your playIntegrityProject. See the Quick Start and Accounts and keys.
  • The platform setup below.

Platform setup

Android: Play Integrity

Play only issues a genuine token to an app it recognizes: installed from Google Play, or signed with a key registered in your app's Play Console entry. Test with a build from a Google Play testing track, on a real device with Google Play services.

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.

import {Platform} from 'react-native';
import * as Dea from '@dea/react-native';

await Dea.init({
  appId: Platform.OS === 'ios' ? 'YOUR_IOS_APP_ID' : 'YOUR_ANDROID_APP_ID',
  appKey: Platform.OS === 'ios' ? 'YOUR_IOS_APP_KEY' : 'YOUR_ANDROID_APP_KEY',
  playIntegrityProject: 123456789012, // read on Android only
});

iOS: App Attest

Add the App Attest capability to your app:

  1. Open ios/YourApp.xcworkspace in Xcode.
  2. Select your app target, then Signing & Capabilities.
  3. Click + Capability and add App Attest.

This adds the com.apple.developer.devicecheck.appattest-environment entitlement. Without the capability, attest() answers failed, and attestation.detail typically mentions invalidKey.

App Attest needs iOS 14 or later on a real device. On the iOS Simulator the registration is sent without an App Attest token, so platformAttested is false.

Usage

Attest

import * as Dea from '@dea/react-native';

const attestation = await Dea.attest();

switch (attestation.verdict) {
  case 'passed':
    // DEA accepted this device's key.
    break;
  case 'unsupported':
    // No key could be created on this device, or no appKey was given. See attestation.detail.
    break;
  case 'failed':
    // DEA refused, or could not be reached. See attestation.detail.
    break;
}
  • attest() does not reject for a refusal, a timeout or a missing keystore: every outcome comes back as an Attestation. It rejects only when init has not run.
  • The result is cached, and survives app restarts, so calling it again costs nothing until shortly before it expires.
  • The first call also turns on automatic renewal. From then on the SDK renews the registration shortly before it expires, and re-plans when the app returns to the foreground. An app that never calls attest() never registers a key.

Check whether the platform vouched for the device

A passed verdict means DEA accepted the key. It is a statement about the device only when platformAttested is true:

if (attestation.verdict === 'passed' && attestation.platformAttested) {
  // Google or Apple confirmed the device and app.
}

platformAttested is false on emulators and simulators, on Android devices without Google Play services, and on builds Play does not recognize.

Read the last result without attesting again

const last = await Dea.getLastAttestation(); // null before the first attest()

Renew now

const renewed = await Dea.renewAttestation();

Renews the registration immediately and re-plans the next automatic renewal. A refusal keeps the registration already held.

Follow automatic renewal

Subscribe before the first attest(), which is what schedules the first renewal:

import * as Dea from '@dea/react-native';

const subscription = Dea.onAttestationEvent(event => {
  switch (event.type) {
    case 'scheduled':
      console.log(`renewal in ${event.afterSeconds} s`);
      break;
    case 'fired':
      console.log('renewed:', event.verdict);
      break;
    case 'idle':
      console.log('nothing to renew');
      break;
  }
});

// later
subscription.remove();

getNextAttestationRefresh() resolves the time of the next automatic renewal, in epoch seconds, or null when none is planned.

Examples

Attest before a payment step

import * as Dea from '@dea/react-native';

export async function openPayment(navigation: {navigate: (route: string) => void}) {
  const attestation = await Dea.attest().catch(() => null);

  if (attestation?.verdict !== 'passed') {
    console.warn('DEA attestation:', attestation);
  }

  navigation.navigate('Payment');
}

API

Member Returns Description
attest() Promise<Attestation> Registers the device key and returns the result; turns on automatic renewal
renewAttestation() Promise<Attestation> Renews now and re-plans automatic renewal
getLastAttestation() Promise<Attestation \| null> The last result, without attesting again
getNextAttestationRefresh() Promise<number \| null> Next automatic renewal, epoch seconds
onAttestationEvent(listener) EventSubscription Every decision automatic renewal makes

Attestation:

Field Type Description
supported boolean Whether a key could be created on this device
verdict 'passed' \| 'failed' \| 'unsupported' The outcome
platformAttested boolean Whether Play Integrity or App Attest vouched for the device
expiresAt number \| null Expiry, in epoch seconds
kid string \| null The key's identifier
tier string \| null The trust tier DEA assigned
backing string \| null Where the key is kept on the device
detail string \| null Why attestation failed, when it did