Install
$ agentstack add skill-iml1s-flutter-claude-skills-firebase-appcheck-manager ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Firebase App Check Manager
> ⚠️ #1 ROOT CAUSE of "App attestation failed": Missing Team ID or API not enabled > > Two things MUST be in place for iOS App Check to work: > 1. firebaseappcheck.googleapis.com API must be ENABLED — even if you've configured providers in Console, if this API isn't enabled, all token exchanges fail silently. > 2. iOS App must have teamId set — Without Team ID, App Attest cannot verify with Apple's servers. > > Team ID can be set via REST API (no Console UI needed): > ``bash > curl -X PATCH -H "Authorization: Bearer $ACCESS_TOKEN" \ > -H "x-goog-user-project: ${PROJECT_ID}" \ > -H "Content-Type: application/json" \ > -d '{"teamId":"YOUR_TEAM_ID"}' \ > "https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/iosApps/${IOS_APP_ID}?updateMask=teamId" > ` > > If Team ID is already set and API is enabled, verify app registration through **Firebase Console → App Check → 應用程式 tab**. > Required IAM role: roles/editor or roles/owner (not just roles/firebase.viewer or roles/firebaseappcheck.admin`).
Manage Firebase App Check configuration entirely via CLI (gcloud + REST API). No Firebase Console UI needed for most steps, except app registration (see Step 0).
Concepts: How App Check Works
App Check is a server-side gatekeeper that protects your Firebase backend services from abuse. It works via a 3-step process:
┌─────────────┐ ① Attestation ┌────────────────────┐
│ Your App │ ──────────────────────→ │ Attestation │
│ (Flutter) │ │ Provider │
│ │ ←──────────────────── │ (Play Integrity / │
│ │ ② Attestation Token │ App Attest) │
│ │ └────────────────────┘
│ │ ③ Exchange for
│ │ App Check Token ┌────────────────────┐
│ │ ──────────────────────→ │ Firebase │
│ │ │ App Check Server │
│ │ ←──────────────────── │ │
│ │ ④ App Check Token └────────────────────┘
│ │
│ │ ⑤ API call with ┌────────────────────┐
│ │ App Check Token │ Firebase Service │
│ │ ──────────────────────→ │ (AI, Firestore, │
│ │ │ Storage, etc.) │
│ │ ⑥ Response └────────────────────┘
└─────────────┘ (or 403 if invalid)
Critical understanding:
- The
activate()call in Flutter just tells the SDK to start sending tokens - Without enforcement on the server side, those tokens are ignored — ANY request is accepted
- Enforcement = server rejects requests without valid App Check tokens
- You must enable enforcement per Firebase service independently
Android: Three Signing Keys
Android apps have three different signing keys. All three produce different SHA-256 fingerprints. All three must be registered in Firebase for App Check to work across dev, testing, and production:
| Key | When Used | Where to Find | |-----|-----------|---------------| | Debug Key | Local flutter run / flutter build --debug | cd android && ./gradlew signingReport | | Upload Key | Your local .jks keystore used for flutter build appbundle --release | Same signingReport or keytool -list -keystore your.jks | | App Signing Key | Google Play Console re-signs your AAB with this key before distributing to users | Google Play Console → Release → Setup → App signing |
> ⚠️ The #1 mistake: Registering only the debug or upload key. Production users receive the app signed with the App Signing Key which is different — their App Check attestation will fail silently.
iOS: App Attest vs DeviceCheck
| Provider | iOS Version | Strength | Recommendation | |----------|-------------|----------|----------------| | AppleAppAttestProvider | iOS 14.0+ | Strong (hardware-backed) | ⚠️ ~25% failure rate in production (FlutterFire #10683) | | AppleDeviceCheckProvider | iOS 11.0+ | Medium | Standalone fallback only | | AppleAppAttestProviderWithDeviceCheckFallback | All | Adaptive | ✅ RECOMMENDED for production |
> ⚠️ CRITICAL: Always use appAttestWithDeviceCheckFallback in production, NOT appAttest alone. > > App Attest depends on Apple's attestation server. Even on iOS 14+ real devices, ExchangeAppAttestAttestation has a ~25% failure rate due to: > - Apple attestation server transient failures > - First-time attestation timing issues on fresh installs > - Network conditions during attestation handshake > - Device state edge cases (not jailbreak related) > > appAttestWithDeviceCheckFallback tries App Attest first, and automatically falls back to DeviceCheck when App Attest fails. Both are Apple official attestation mechanisms with equivalent security for App Check purposes. This ensures near-100% token acquisition rate. > > Firebase Console must have both App Attest AND DeviceCheck providers registered for the iOS app.
> iOS entitlement required: You MUST add com.apple.developer.devicecheck.appattest-environment to your iOS entitlements for App Attest to work in production builds.
Prerequisites
gcloudCLI authenticated with the correct account (gcloud auth list/gcloud config set account EMAIL)- Owner/Editor role on the Firebase project
- Know your Firebase Project ID and App IDs (
firebase apps:list --project=PROJECT_ID)
Common Header Pattern
All REST API calls need these headers:
ACCESS_TOKEN=$(gcloud auth print-access-token)
PROJECT_ID="your-project-id"
ANDROID_APP_ID="1:xxxx:android:xxxx"
IOS_APP_ID="1:xxxx:ios:xxxx"
PROJECT_NUMBER="123456789" # from firebase projects:list
-H "Authorization: Bearer $ACCESS_TOKEN"
-H "x-goog-user-project: ${PROJECT_ID}"
-H "Content-Type: application/json"
> Important: The x-goog-user-project header is required for quota billing. Without it you get SERVICE_DISABLED errors.
Quick Audit Script (One-Shot Health Check)
Run this script to get a full status of App Check for any Firebase project. Copy/paste the entire block:
#!/bin/bash
# Usage: Set the 5 variables below, then run the entire script
ACCESS_TOKEN=$(gcloud auth print-access-token)
PROJECT_ID="your-project-id"
PROJECT_NUMBER="123456789"
ANDROID_APP_ID="1:xxxx:android:xxxx"
IOS_APP_ID="1:xxxx:ios:xxxx"
echo "╔═══════════════════════════════════════════╗"
echo "║ Firebase App Check Audit: $PROJECT_ID ║"
echo "╚═══════════════════════════════════════════╝"
echo ""
# 1. API Enablement
echo "── 1. API Enablement ──"
AC=$(gcloud services list --enabled --filter="config.name:firebaseappcheck" --project=$PROJECT_ID --format="value(config.name)" 2>/dev/null)
PI=$(gcloud services list --enabled --filter="config.name:playintegrity" --project=$PROJECT_ID --format="value(config.name)" 2>/dev/null)
[ -n "$AC" ] && echo " ✅ firebaseappcheck.googleapis.com ENABLED" || echo " ❌ firebaseappcheck.googleapis.com NOT ENABLED"
[ -n "$PI" ] && echo " ✅ playintegrity.googleapis.com ENABLED" || echo " ❌ playintegrity.googleapis.com NOT ENABLED"
echo ""
# 2. Services Enforcement
echo "── 2. Services Enforcement ──"
SERVICES=$(curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_NUMBER}/services")
if echo "$SERVICES" | python3 -c "import sys,json; d=json.load(sys.stdin); svcs=d.get('services',[]); [print(f' {s[\"name\"].split(\"/\")[-1]}: {s.get(\"enforcementMode\",\"UNENFORCED\")}') for s in svcs]" 2>/dev/null; then
:
else
echo " ⚠️ No services configured or empty response"
fi
echo ""
# 3. Android Play Integrity Config
echo "── 3. Android Play Integrity Config ──"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_NUMBER}/apps/${ANDROID_APP_ID}/playIntegrityConfig" | python3 -c "
import sys,json
d=json.load(sys.stdin)
ttl=d.get('tokenTtl','?')
lvl=d.get('deviceIntegrity',{}).get('minDeviceRecognitionLevel','?')
print(f' Token TTL: {ttl}')
print(f' Min Device Recognition: {lvl}')
if lvl == 'NO_INTEGRITY':
print(' ⚠️ NO_INTEGRITY allows any device (including emulators)')
" 2>/dev/null
echo ""
# 4. iOS App Attest Config
echo "── 4. iOS App Attest Config ──"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_NUMBER}/apps/${IOS_APP_ID}/appAttestConfig" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f' Token TTL: {d.get(\"tokenTtl\",\"?\")}')
" 2>/dev/null
echo ""
# 4b. iOS App Team ID (CRITICAL for App Attest)
echo "── 4b. iOS App Team ID ──"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/iosApps/${IOS_APP_ID}" | python3 -c "
import sys,json
d=json.load(sys.stdin)
tid=d.get('teamId','')
if tid:
print(f' ✅ Team ID: {tid}')
else:
print(' ❌ Team ID: NOT SET — App Attest WILL FAIL!')
print(' Fix: curl -X PATCH ... -d \'{\"teamId\":\"YOUR_TEAM_ID\"}\' .../iosApps/APP_ID?updateMask=teamId')
" 2>/dev/null
echo ""
# 5. Android SHA Fingerprints (SHA-1 + SHA-256)
echo "── 5. Android SHA Fingerprints ──"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/androidApps/${ANDROID_APP_ID}/sha" | python3 -c "
import sys,json
d=json.load(sys.stdin)
certs=d.get('certificates',[])
sha1 = [c for c in certs if c.get('certType')=='SHA_1']
sha256 = [c for c in certs if c.get('certType')=='SHA_256']
print(f' Total: {len(certs)} ({len(sha1)} SHA-1, {len(sha256)} SHA-256)')
for cert in certs:
print(f' - {cert.get(\"certType\",\"?\"):8s} : {cert.get(\"shaHash\",\"?\")}')
if len(sha256) /dev/null
echo ""
# 6. Debug Tokens
echo "── 6. Debug Tokens ──"
echo " Android:"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_ID}/apps/${ANDROID_APP_ID}/debugTokens" | python3 -c "
import sys,json
d=json.load(sys.stdin)
tokens=d.get('debugTokens',[])
print(f' Count: {len(tokens)}')
for t in tokens:
print(f' - {t.get(\"displayName\",\"unnamed\")} ({t[\"name\"].split(\"/\")[-1]})')
" 2>/dev/null
echo " iOS:"
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_ID}/apps/${IOS_APP_ID}/debugTokens" | python3 -c "
import sys,json
d=json.load(sys.stdin)
tokens=d.get('debugTokens',[])
print(f' Count: {len(tokens)}')
for t in tokens:
print(f' - {t.get(\"displayName\",\"unnamed\")} ({t[\"name\"].split(\"/\")[-1]})')
" 2>/dev/null
echo ""
echo "── Audit Complete ──"
Step-by-Step Setup
Step 0: Register App for App Check
Option A: Via REST API / CLI (Preferred)
You can now configure App Check entirely via CLI:
1. Set iOS Team ID (required for App Attest):
# Check if Team ID is already set
curl -s -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/iosApps/${IOS_APP_ID}" | python3 -c "
import sys,json; d=json.load(sys.stdin); print(f'teamId: {d.get(\"teamId\",\"NOT SET\")}')"
# Set Team ID if missing
curl -X PATCH -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
-H "Content-Type: application/json" \
-d '{"teamId":"YOUR_APPLE_TEAM_ID"}' \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/iosApps/${IOS_APP_ID}?updateMask=teamId"
2. Configure App Attest (iOS):
curl -X PATCH -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
-H "Content-Type: application/json" \
-d '{"tokenTtl":"3600s"}' \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_NUMBER}/apps/${IOS_APP_ID}/appAttestConfig?updateMask=tokenTtl"
3. Configure DeviceCheck (iOS fallback):
curl -X PATCH -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
-H "Content-Type: application/json" \
-d '{"tokenTtl":"3600s"}' \
"https://firebaseappcheck.googleapis.com/v1/projects/${PROJECT_NUMBER}/apps/${IOS_APP_ID}/deviceCheckConfig?updateMask=tokenTtl"
Option B: Via Firebase Console UI
If the REST API approach doesn't fully register the app (status still shows "未註冊"):
- Go to Firebase Console → your project → App Check → 應用程式 (Apps) tab
- If the app's status shows "未註冊" (Not registered), click the app row to expand it
- Select the attestation provider:
- iOS: Select App Attest (and optionally DeviceCheck for fallback)
- Android: Select Play Integrity
- For iOS: Fill in your Apple Team ID (e.g. `` (10-character alphanumeric) — find it at Apple Developer → Membership)
- Set the token TTL (default 1 hour is fine for most apps)
- Click Save
- Verify the status changes from "未註冊" to "已註冊" (Registered)
- Repeat for each app (iOS, Android) that needs App Check
> IAM Role Requirement: This operation requires roles/editor or roles/owner on the Firebase project. roles/firebase.viewer or even roles/firebaseappcheck.admin is NOT sufficient. Grant the required role with: > ``bash > gcloud projects add-iam-policy-binding PROJECT_ID --member="user:EMAIL" --role="roles/editor" > ``
Step 1: Enable Required APIs
# Enable App Check API
gcloud services enable firebaseappcheck.googleapis.com --project=${PROJECT_ID}
# Enable Play Integrity API (required for Android attestation)
gcloud services enable playintegrity.googleapis.com --project=${PROJECT_ID}
Step 2: Register Android SHA-256 Fingerprints
You need three SHA-256 fingerprints for Android:
| Key Type | Where to Find | |----------|---------------| | Debug Key | ./gradlew signingReport in android/ | | Upload Key | Your local release .jks file (also in signingReport) | | App Signing Key | Google Play Console → Release → App Integrity → App Signing |
List Current Fingerprints
curl -s \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/androidApps/${ANDROID_APP_ID}/sha"
Add a SHA-256 Fingerprint
Convert the colon-separated format to lowercase hex (remove colons): 64:50:0E:EF:... → 64500eef...
curl -s -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "x-goog-user-project: ${PROJECT_ID}" \
-H "Content-Type: application/json" \
-d '{"shaHash":"LOWERCASE_HEX_NO_COLONS","certType":"SHA_256"}' \
"https://firebase.googleapis.com/v1beta1/projects/${PROJECT_ID}/androidApps/${ANDROID_APP_ID}/sha"
Getting All 3 SHA-256 Fingerprints
> ⚠️ ./gradlew signingReport prerequisite: This command requires Flutter plugin cache to exist. Run flutter pub get first, otherwise Gradle fails with Plugin directory does not exist.
> ⚠️ keytool locale issue: On non-English systems (e.g. Chinese locale), keytool -list -v outputs labels in the local language (e.g. 憑證指紋 instead of Certificate fingerprints). Use grep SHA or pipe through python instead of grep SHA-256.
Recommended: Direct keytool approach (locale-agnostic, no Gradle needed)
# Debug key — default Android debug keystore
keytool -list -v -keystore ~/.android/debug.keystore -storepass
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [ImL1s](https://github.com/ImL1s)
- **Source:** [ImL1s/flutter-claude-skills](https://github.com/ImL1s/flutter-claude-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.