expo-app-integrity
Version: 55.0.6
A native module that helps assert app integrity on mobile platforms using Apple’s App Attest and Google’s Play Integrity APIs. Verify that your app hasn’t been tampered with and is running on a genuine device.
Installation
npx expo install expo-app-integrity
Usage
import * as AppIntegrity from 'expo-app-integrity';
// Check if integrity checking is available
const isAvailable = await AppIntegrity.isAvailableAsync();
if (isAvailable) {
// Request attestation
const token = await AppIntegrity.getAttestationAsync('challenge-from-server');
// Send token to your server for verification
await verifyWithServer(token);
}
API Reference
Methods
Checks if app integrity verification is availableReturns true on iOS 14+ and Android with Play Servicesconst available = await AppIntegrity.isAvailableAsync();
if (!available) {
console.log('Integrity checking not available');
}
getAttestationAsync(challenge)
(challenge: string) => Promise<string>
Requests attestation tokenParameters:
challenge (string): Challenge string from your server
Returns: Attestation token to verify on serverconst challenge = await fetchChallengeFromServer();
const token = await AppIntegrity.getAttestationAsync(challenge);
// Send token to server for verification
const isValid = await verifyTokenOnServer(token);
Examples
Basic Integrity Check
import * as AppIntegrity from 'expo-app-integrity';
async function verifyAppIntegrity() {
const isAvailable = await AppIntegrity.isAvailableAsync();
if (!isAvailable) {
console.log('App integrity checking not supported');
return false;
}
try {
// Get challenge from your server
const challenge = await fetch('https://api.example.com/challenge')
.then(res => res.text());
// Get attestation token
const token = await AppIntegrity.getAttestationAsync(challenge);
// Verify token with your server
const response = await fetch('https://api.example.com/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});
const { valid } = await response.json();
return valid;
} catch (error) {
console.error('Integrity check failed:', error);
return false;
}
}
Protect API Calls
import * * AppIntegrity from 'expo-app-integrity';
async function makeSecureAPICall(endpoint: string, data: any) {
// Get attestation before making sensitive API call
const challenge = Date.now().toString();
const attestation = await AppIntegrity.getAttestationAsync(challenge);
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Attestation': attestation,
'X-Challenge': challenge,
},
body: JSON.stringify(data),
});
return response.json();
}
Complete Example with Server Verification
import * as AppIntegrity from 'expo-app-integrity';
import { useState, useEffect } from 'react';
import { View, Button, Text, StyleSheet, ActivityIndicator } from 'react-native';
export default function IntegrityExample() {
const [isAvailable, setIsAvailable] = useState(false);
const [isVerified, setIsVerified] = useState<boolean | null>(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
checkAvailability();
}, []);
async function checkAvailability() {
const available = await AppIntegrity.isAvailableAsync();
setIsAvailable(available);
}
async function verifyIntegrity() {
setLoading(true);
setIsVerified(null);
try {
// Step 1: Get challenge from server
const challengeResponse = await fetch(
'https://your-api.com/integrity/challenge'
);
const { challenge } = await challengeResponse.json();
// Step 2: Get attestation token
const token = await AppIntegrity.getAttestationAsync(challenge);
// Step 3: Verify with server
const verifyResponse = await fetch(
'https://your-api.com/integrity/verify',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token, challenge }),
}
);
const { valid } = await verifyResponse.json();
setIsVerified(valid);
} catch (error) {
console.error('Verification error:', error);
setIsVerified(false);
} finally {
setLoading(false);
}
}
if (!isAvailable) {
return (
<View style={styles.container}>
<Text style={styles.message}>
App integrity checking is not available on this device
</Text>
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>App Integrity Check</Text>
{loading ? (
<ActivityIndicator size="large" />
) : (
<>
<Button title="Verify App Integrity" onPress={verifyIntegrity} />
{isVerified !== null && (
<View style={styles.result}>
{isVerified ? (
<>
<Text style={styles.success}>✓ Verified</Text>
<Text>App integrity check passed</Text>
</>
) : (
<>
<Text style={styles.error}>✗ Failed</Text>
<Text>App integrity check failed</Text>
</>
)}
</View>
)}
</>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
gap: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
},
message: {
fontSize: 16,
textAlign: 'center',
color: '#666',
},
result: {
alignItems: 'center',
gap: 10,
},
success: {
fontSize: 48,
color: '#4CAF50',
},
error: {
fontSize: 48,
color: '#F44336',
},
});
| Platform | API Used | Minimum Version |
|---|
| iOS | App Attest | iOS 14+ |
| Android | Play Integrity | Android 4.4+ with Play Services |
| Web | ❌ Not available | - |
Server-Side Verification
iOS (App Attest)
// Node.js example with Apple's App Attest API
const jwt = require('jsonwebtoken');
const { appleAppAttest } = require('apple-app-attest');
async function verifyiOSAttestation(token, challenge) {
try {
const decoded = jwt.decode(token, { complete: true });
// Verify with Apple's servers
const result = await appleAppAttest.verifyAttestation({
attestation: token,
challenge,
bundleId: 'com.yourapp.bundle',
});
return result.valid;
} catch (error) {
console.error('iOS attestation verification failed:', error);
return false;
}
}
Android (Play Integrity)
// Node.js example with Google Play Integrity API
const { google } = require('googleapis');
async function verifyAndroidAttestation(token, challenge) {
try {
const playIntegrity = google.playintegrity('v1');
const response = await playIntegrity.decodeIntegrityToken({
packageName: 'com.yourapp.package',
requestBody: { integrityToken: token },
});
// Check integrity verdict
const { appIntegrity, deviceIntegrity } = response.data.tokenPayloadExternal;
return (
appIntegrity.appRecognitionVerdict === 'PLAY_RECOGNIZED' &&
deviceIntegrity.deviceRecognitionVerdict.includes('MEETS_DEVICE_INTEGRITY')
);
} catch (error) {
console.error('Android attestation verification failed:', error);
return false;
}
}
Configuration
iOS
No additional configuration required. App Attest is automatically available on iOS 14+.
Android
Add to app.json:
{
"expo": {
"android": {
"package": "com.yourapp.package"
},
"plugins": [
[
"expo-app-integrity",
{
"androidCloudProjectNumber": "YOUR_PROJECT_NUMBER"
}
]
]
}
}
Enable Play Integrity API in Google Cloud Console.
Use Cases
- Anti-Tampering: Detect if app has been modified or repackaged
- Emulator Detection: Identify if app is running on emulator
- Root/Jailbreak Detection: Detect rooted or jailbroken devices
- API Protection: Verify app integrity before sensitive operations
- License Verification: Ensure app is from official store
- Fraud Prevention: Prevent abuse from modified apps
Best Practices
- Server Verification: Always verify attestation tokens on your server
- Fresh Challenges: Use unique, time-limited challenges
- Graceful Degradation: Handle unavailable integrity checks gracefully
- Rate Limiting: Limit attestation requests to prevent abuse
- Secure Storage: Store attestation keys securely
- Error Handling: Handle errors without revealing security details
Never rely solely on client-side integrity checks. Always verify attestation tokens on your secure server.
Limitations
- iOS: Requires iOS 14 or later
- Android: Requires Google Play Services
- Rate Limits: Both platforms have rate limits on attestation requests
- No Web Support: Not available for web platforms
- Not Foolproof: Can be bypassed by sophisticated attackers
App integrity checks provide an additional layer of security but are not foolproof. Use them as part of a comprehensive security strategy.
Resources