app_device_integrity_plus 1.0.0
app_device_integrity_plus: ^1.0.0 copied to clipboard
Flutter plugin for App Attest and Play Integrity with real challenge (nonce) support. Replaces static dummy nonce with server-generated values.
App Device Integrity Plus #
πΊπΈ English
Why? #
What This Fork Fixes #
The original app_device_integrity had a critical issue:
-
The challengeString (nonce) sent from Flutter was completely ignored.
-
The plugin always used a static Base64.encode(ByteArray(40)) value.
-
This produced the well-known "AAAAAAAAAAAA..." nonce in Play Integrity logs.
-
Real server-side verification was not possible because the nonce never matched.
Fixes in This Version #
This fork fixes all of that.
-
Proper nonce passthrough from Flutter β Native β Play Integrity
-
Removed static dummy nonce (ByteArray(40))
-
Added proper MethodChannel argument handling
-
Updated API to accept challengeString exactly as given
-
Real attestation with server-side validation now works
-
README rewritten for clarity
-
Additional flow diagram for easier understanding
π How to Use #
- Request nonce from your backend
Your server must generate a unique challenge per session.
final sessionId = await api.getNonce();
- Pass nonce into the plugin
final integrity = AppDeviceIntegrityPlus();
if (Platform.isAndroid) {
final token = await integrity.getAttestationServiceSupport(
challengeString: sessionId,
gcp: 523725941100,
);
} else {
final token = await integrity.getAttestationServiceSupport(
challengeString: sessionId,
);
}
- Send token to backend for validation
await api.verifyIntegrity(token);
π Attestation Flow (App β API Server β Google) #
sequenceDiagram
participant APP
participant API as API Server
participant GOOGLE as Google Server
APP->>API: Request nonce
API-->>APP: Issue nonce (server-generated value)
APP->>GOOGLE: Call Play Integrity API (with nonce)
GOOGLE-->>APP: Return signed token (JWT)
APP->>API: Send token for verification
note right of API: Backend verification begins
API->>API: 1. Verify JWT signature using cached Google public keys
API->>API: 2. Decode token payload
API->>API: 3. Validate nonce, timestamp, package name, etc.
note right of API: Verification complete
API-->>APP: OK (trusted device/app) or Error
π References #
π°π· νκ΅μ΄
π§ μ λ§λ€κ² λμλκ°? #
μλ³Έ app_device_integrity νλ¬κ·ΈμΈμ λ¬Έμ #
- Flutterμμ λκΈ΄ challengeString(nonce)μ μ ν μ¬μ©νμ§ μμ
- λ΄λΆμμ νμ ByteArray(40) β Base64 μΈμ½λ©ν κ° μ¬μ©
- κ·Έλμ Play Integrity λ‘κ·Έμ "AAAAAAAAAA..." nonceλ§ μΆλ ₯λ¨
- μλ² κ²μ¦ μ nonce λΆμΌμΉ β μ μμ μΈ λ³΄μ κ²μ¦ λΆκ°λ₯
β μ΄ λ²μ μμ μμ / κ°μ λ λ΄μ© #
μ΄ ν¬ν¬λ μ΄ λ¬Έμ λ₯Ό μ λΆ ν΄κ²°ν©λλ€.
-
μλ²μμ λ°μ nonceλ₯Ό κ·Έλλ‘ Play Integrityμ μ λ¬
-
λ μ΄μ static dummy nonce μ¬μ©νμ§ μμ
-
MethodChannel νλΌλ―Έν° μ²λ¦¬ μμ
-
Android/iOSμμ μ€μ nonce κΈ°λ° ν ν° μμ± κ°λ₯
-
README μ λ©΄ μ¬μμ±
-
νλ‘μ° λ€μ΄μ΄κ·Έλ¨ μΆκ°
π μ¬μ© λ°©λ² #
- μλ²μμ nonce λ°κΈ
final sessionId = await api.getNonce();
- νλ¬κ·ΈμΈμ nonce μ λ¬
final integrity = AppDeviceIntegrityPlus();
if (Platform.isAndroid) {
final token = await integrity.getAttestationServiceSupport(
challengeString: sessionId,
gcp: 523725941100,
);
} else {
final token = await integrity.getAttestationServiceSupport(
challengeString: sessionId,
);
}
- ν ν°μ μλ²λ‘ μ λ¬ν΄ κ²μ¦
await api.verifyIntegrity(token);
π μ 체 νλ‘μ° (μ± β μλ² β Google) #
sequenceDiagram
participant APP
participant API as API Server
participant GOOGLE as Google Server
APP->>API: nonce λ°κΈ μμ²
API-->>APP: nonce λ°κΈ (κ³ μ κ° μμ±)
APP->>GOOGLE: Play Integrity API νΈμΆ (nonce ν¬ν¨)
GOOGLE-->>APP: signed token λ°ν (JWT)
APP->>API: token μ λ¬ (κ²μ¦ μμ²)
note right of API: API μλ²(λ°±μλ)μμ λ‘컬 κ²μ¦ μμ
API->>API: 1. (μΊμλ) Google Public Keyλ‘ ν ν° μλͺ
κ²μ¦
API->>API: 2. ν ν° payload λμ½λ©
API->>API: 3. nonce, timestamp, μ± μ 보(ν¨ν€μ§λͺ
) λ± νμΈ
note right of API: κ²μ¦ μλ£
API-->>APP: OK (μ ν μ±/μ λ’°ν μ μμ) λλ Error