@qafka/react-native 2.8.1 → 2.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.ht
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.8.2] — 2026-11-12
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Android emulator in development builds: initialization failed with `Attestation failed: 401 — Root certificate is not a Google attestation root`, because emulators report Android 8+ but have no hardware-backed keystore. In development builds (`__DEV__`) the SDK now detects the emulator and continues without attestation, the same way it already does on the iOS simulator. Use a TEST API key there; PRODUCTION keys still require an attested session. Release builds are unchanged and keep attesting on every device. Requires a native rebuild.
|
|
15
|
+
|
|
10
16
|
## [2.8.1] — 2026-10-06
|
|
11
17
|
|
|
12
18
|
### Added
|
|
@@ -27,9 +27,31 @@ class QafkaAttestationModule(reactContext: ReactApplicationContext) :
|
|
|
27
27
|
"bundleId" to bundleId,
|
|
28
28
|
"appVersion" to appVersion,
|
|
29
29
|
"deviceModel" to deviceModel,
|
|
30
|
+
"isEmulator" to isEmulator(),
|
|
30
31
|
)
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
// Emulators have no TEE, so their attestation chain is rooted in a
|
|
35
|
+
// software root that the backend rejects. JS uses this only in
|
|
36
|
+
// development builds to fall back to the unsupported-device path.
|
|
37
|
+
private fun isEmulator(): Boolean {
|
|
38
|
+
val fingerprint = Build.FINGERPRINT ?: ""
|
|
39
|
+
val hardware = Build.HARDWARE ?: ""
|
|
40
|
+
val product = Build.PRODUCT ?: ""
|
|
41
|
+
val model = Build.MODEL ?: ""
|
|
42
|
+
return fingerprint.startsWith("generic") ||
|
|
43
|
+
fingerprint.startsWith("unknown") ||
|
|
44
|
+
fingerprint.contains("emulator") ||
|
|
45
|
+
hardware == "goldfish" ||
|
|
46
|
+
hardware == "ranchu" ||
|
|
47
|
+
product.startsWith("sdk") ||
|
|
48
|
+
product.contains("_sdk") ||
|
|
49
|
+
product.contains("emulator") ||
|
|
50
|
+
product.contains("simulator") ||
|
|
51
|
+
model.contains("Emulator") ||
|
|
52
|
+
model.contains("Android SDK built for")
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
@ReactMethod
|
|
34
56
|
fun isSupported(promise: Promise) {
|
|
35
57
|
// Key Attestation requires Android 8.0+ (API 26) for hardware-backed
|
|
@@ -14,6 +14,11 @@ export interface QafkaDeviceInfo {
|
|
|
14
14
|
*/
|
|
15
15
|
export declare function getDeviceInfo(): QafkaDeviceInfo;
|
|
16
16
|
export declare function isAttestationAvailable(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* True when the native module reports an Android emulator. iOS simulators
|
|
19
|
+
* already report `isSupported() === false`, so they never need this flag.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isEmulator(): boolean;
|
|
17
22
|
export declare function isSupported(): Promise<boolean>;
|
|
18
23
|
export declare function generateKey(): Promise<string>;
|
|
19
24
|
export declare function attestKey(keyId: string, challengeBase64: string): Promise<string>;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Platform = void 0;
|
|
4
4
|
exports.getDeviceInfo = getDeviceInfo;
|
|
5
5
|
exports.isAttestationAvailable = isAttestationAvailable;
|
|
6
|
+
exports.isEmulator = isEmulator;
|
|
6
7
|
exports.isSupported = isSupported;
|
|
7
8
|
exports.generateKey = generateKey;
|
|
8
9
|
exports.attestKey = attestKey;
|
|
@@ -31,6 +32,14 @@ function getDeviceInfo() {
|
|
|
31
32
|
function isAttestationAvailable() {
|
|
32
33
|
return NativeAttestation !== null;
|
|
33
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* True when the native module reports an Android emulator. iOS simulators
|
|
37
|
+
* already report `isSupported() === false`, so they never need this flag.
|
|
38
|
+
*/
|
|
39
|
+
function isEmulator() {
|
|
40
|
+
const mod = react_native_1.NativeModules.QafkaAttestation;
|
|
41
|
+
return mod?.isEmulator === true;
|
|
42
|
+
}
|
|
34
43
|
async function isSupported() {
|
|
35
44
|
if (!NativeAttestation)
|
|
36
45
|
return false;
|
|
@@ -74,12 +74,18 @@ class AttestationManager {
|
|
|
74
74
|
// is essential for diagnosing release-build failures where the JS-side
|
|
75
75
|
// error overlay only shows a generic "App configuration error".
|
|
76
76
|
this.log(`[Qafka:init] platform=${react_native_1.Platform.OS} moduleLinked=${moduleLinked}`, `[Qafka:init] platform=${react_native_1.Platform.OS} url=${this.config.apiUrl} moduleLinked=${moduleLinked}`);
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
// Android emulators report Android 8+ but cannot produce a Google-rooted
|
|
78
|
+
// attestation. Only development builds treat them as unsupported; release
|
|
79
|
+
// builds keep attesting so an emulator is never let through unverified.
|
|
80
|
+
const devEmulator = __DEV__ && react_native_1.Platform.OS === 'android' && NativeAttestation.isEmulator();
|
|
81
|
+
const supported = devEmulator ? false : await NativeAttestation.isSupported();
|
|
82
|
+
this.log(`[Qafka:init] isSupported=${supported} devEmulator=${devEmulator}`);
|
|
79
83
|
if (!supported) {
|
|
80
84
|
const reason = !moduleLinked
|
|
81
85
|
? `the QafkaAttestation native module is not registered (rebuild your app after installing the SDK)`
|
|
82
|
-
:
|
|
86
|
+
: devEmulator
|
|
87
|
+
? `Android emulators cannot produce hardware-backed attestation; use a TEST API key`
|
|
88
|
+
: `this device does not support attestation (Android 8.0+ / iOS 14+ required)`;
|
|
83
89
|
if (__DEV__) {
|
|
84
90
|
console.warn(`[Qafka] Attestation unavailable in this dev environment (${reason}). ` +
|
|
85
91
|
`Continuing without it; see the Qafka docs for local development setup.`);
|
package/package.json
CHANGED