app_check_secure 0.0.1
app_check_secure: ^0.0.1 copied to clipboard
A simple, production-ready Flutter package that completely abstracts Firebase App Check for Android, iOS, and Web.
app_check_secure #
A simple, production-ready Flutter package that completely abstracts Firebase App Check.
Your application never talks to Firebase App Check directly — it only uses AppCheckSecure.
Supported platforms: Android · iOS · Web
Features #
- Single public API:
AppCheckSecure - Opt-in platforms:
enableAndroid/enableIos/enableWeb(defaultfalse) - Automatic provider selection via
debug(optional overrides) - Debug token helpers for Android, iOS, and Web (config or setters)
- Android providers: Debug, Play Integrity
- iOS providers: Debug, DeviceCheck, App Attest, App Attest + DeviceCheck fallback
- Web providers: Debug, reCAPTCHA v3, reCAPTCHA Enterprise
- Token cache, refresh, clear, and change stream
- Optional logging with
[AppCheck]prefix - Package-owned exceptions (Firebase errors are never exposed)
- Retry with configurable count and delay
Installation #
1. Add the package #
dependencies:
app_check_secure: ^0.0.1
firebase_core: ^4.0.0
Then run:
flutter pub get
firebase_app_checkis pulled in transitively by this package. Your app still needsfirebase_coreto initialize Firebase.
2. Configure Firebase for your app #
Follow the official FlutterFire setup:
Ensure google-services.json (Android), GoogleService-Info.plist (iOS), and web Firebase options are in place.
Firebase setup #
- Open the Firebase Console.
- Select your project → Build / Security → App Check.
- Register each app (Android, iOS, Web) with the appropriate provider.
- Start in Monitoring mode, then enable Enforcement for Storage, Firestore, Functions, etc. when ready.
- For local development, register debug tokens under App Check → Manage debug tokens.
Android setup #
Play Integrity (production) #
- In Firebase Console → App Check, register your Android app with Play Integrity.
- Add your app’s SHA-256 certificate fingerprints in Project Settings.
- Use production mode (
debug: false) — the package selects Play Integrity automatically:
debug: false,
enableAndroid: true,
Debug (development / CI) #
- Use
debug: true(package selects the Android debug provider). - Either:
- Run once and copy the token from Logcat, or
- Pass a known token via
androidDebugToken/AppCheckSecure.setAndroidDebugToken.
- Register that token in Firebase Console → App Check → Manage debug tokens.
debug: true,
enableAndroid: true,
androidDebugToken: 'YOUR_REGISTERED_DEBUG_TOKEN', // optional
enableLogging: true,
SafetyNet (legacy) #
AndroidProviderType.safetyNet may be passed as an optional override, but SafetyNet is no longer supported by the FlutterFire App Check SDK. Prefer Play Integrity. Selecting SafetyNet throws a ConfigurationException.
iOS setup #
DeviceCheck / App Attest #
- In Firebase Console → App Check, register your iOS app with DeviceCheck or App Attest.
- Enable App Attest capability in Xcode when using App Attest (iOS 14+).
- Ensure your Apple Developer team and App ID are correctly configured.
- With
debug: false, the package defaults to App Attest with DeviceCheck fallback. Override withappleProviderif needed.
debug: false,
enableIos: true,
// optional: appleProvider: AppleProviderType.deviceCheck,
Debug (development / CI) #
- Use
debug: true(package selects the Apple debug provider). - Either copy the token from Xcode / device logs, or pass
iosDebugToken/AppCheckSecure.setIosDebugToken. - Register it in Firebase Console → Manage debug tokens.
debug: true,
enableIos: true,
iosDebugToken: 'YOUR_REGISTERED_DEBUG_TOKEN', // optional
enableLogging: true,
Web setup #
Production (reCAPTCHA) #
- In Firebase Console → App Check, register your web app with reCAPTCHA v3 or reCAPTCHA Enterprise.
- Copy the site key.
- Pass it via
webSiteKey(required on web when not using the debug provider).
enableWeb: true,
debug: false,
webProvider: WebProviderType.recaptchaV3, // optional; this is the default
webSiteKey: 'YOUR_RECAPTCHA_SITE_KEY',
Or:
enableWeb: true,
webProvider: WebProviderType.recaptchaEnterprise,
webSiteKey: 'YOUR_RECAPTCHA_ENTERPRISE_SITE_KEY',
Do not hardcode production keys in source control when avoidable — use --dart-define or your secrets pipeline.
Debug (development / CI) #
- Use
debug: true(package selects the Web debug provider;webSiteKeynot required). - Pass
webDebugToken/AppCheckSecure.setWebDebugToken, or let the SDK print one in the browser console. - Register the token in Firebase Console → Manage debug tokens.
enableWeb: true,
debug: true,
webDebugToken: 'YOUR_REGISTERED_DEBUG_TOKEN', // optional
enableLogging: true,
Usage #
Import only the package entry point:
import 'package:app_check_secure/app_check_secure.dart';
Initialize (after Firebase) #
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Optional: set debug tokens before initialize (CI / known devices).
// AppCheckSecure.setAndroidDebugToken('...');
// AppCheckSecure.setIosDebugToken('...');
// AppCheckSecure.setWebDebugToken('...');
await AppCheckSecure.initialize(
config: AppCheckConfig(
debug: kDebugMode,
// Opt-in: only enable platforms you need (defaults are false).
enableAndroid: true,
enableIos: true,
enableWeb: true,
webSiteKey: const String.fromEnvironment('RECAPTCHA_SITE_KEY'),
enableLogging: true,
autoRefresh: true,
),
);
runApp(const MyApp());
}
The package detects the current device platform, checks the matching enable* flag, and selects providers from debug (unless you pass an override).
Debug tokens #
// Before initialize (optional)
AppCheckSecure.setAndroidDebugToken('...');
AppCheckSecure.setIosDebugToken('...');
AppCheckSecure.setWebDebugToken('...');
print(AppCheckSecure.getAndroidDebugToken());
print(AppCheckSecure.getIosDebugToken());
print(AppCheckSecure.getWebDebugToken());
print(AppCheckSecure.getDebugToken()); // current platform
AppCheckSecure.clearDebugTokens();
Or pass them in config: androidDebugToken / iosDebugToken / webDebugToken
(config values win when both config and setters are used).
Get / refresh / clear App Check token #
final token = await AppCheckSecure.getToken();
final fresh = await AppCheckSecure.refreshToken();
await AppCheckSecure.clearToken();
print(AppCheckSecure.currentToken);
print(AppCheckSecure.isInitialized);
Listen for token changes #
AppCheckSecure.onTokenChanged.listen((token) {
// Handle updated or cleared token
});
Dispose #
await AppCheckSecure.dispose();
Configuration #
| Field | Type | Default | Description |
|---|---|---|---|
debug |
bool |
false |
Use debug providers (unless overridden) |
enableAndroid |
bool |
false |
Allow App Check when running on Android |
enableIos |
bool |
false |
Allow App Check when running on iOS |
enableWeb |
bool |
false |
Allow App Check when running on Web |
webSiteKey |
String? |
null |
Required on web for reCAPTCHA (not needed for debug) |
webProvider |
WebProviderType |
recaptchaV3 |
Web attestation provider (ignored when debug: true) |
androidProvider |
AndroidProviderType? |
null |
Optional Android override |
appleProvider |
AppleProviderType? |
null |
Optional iOS override |
androidDebugToken |
String? |
null |
Optional Android debug token |
iosDebugToken |
String? |
null |
Optional iOS debug token |
webDebugToken |
String? |
null |
Optional Web debug token |
enableLogging |
bool |
false |
Enables [AppCheck] logs |
autoRefresh |
bool |
true |
Firebase automatic token refresh |
forceRefresh |
bool |
false |
Default for getToken() when forceRefresh is omitted |
retryCount |
int |
3 |
Extra retries after the first failure (0 = one attempt) |
retryDelay |
Duration |
2s |
Delay between retries |
Automatic provider selection #
debug |
Android (default) | iOS (default) | Web (default) |
|---|---|---|---|
true |
Debug | Debug | Debug |
false |
Play Integrity | App Attest + DeviceCheck fallback | reCAPTCHA (webProvider) |
Provider override enums #
Android: debug · playIntegrity · safetyNet (unsupported — throws)
Apple: debug · deviceCheck · appAttest · appAttestWithDeviceCheckFallback
Web: debug · recaptchaV3 · recaptchaEnterprise
Examples #
Production config #
const config = AppCheckConfig(
debug: false,
enableAndroid: true,
enableIos: true,
enableWeb: true,
webSiteKey: String.fromEnvironment('RECAPTCHA_SITE_KEY'),
enableLogging: false,
autoRefresh: true,
);
Debug config #
const config = AppCheckConfig(
debug: true,
enableAndroid: true,
enableIos: true,
enableWeb: true,
androidDebugToken: String.fromEnvironment('ANDROID_APP_CHECK_DEBUG_TOKEN'),
iosDebugToken: String.fromEnvironment('IOS_APP_CHECK_DEBUG_TOKEN'),
webDebugToken: String.fromEnvironment('WEB_APP_CHECK_DEBUG_TOKEN'),
enableLogging: true,
);
Enable only some platforms #
final config = AppCheckConfig(
debug: kDebugMode,
enableAndroid: true,
enableIos: true,
// enableWeb defaults to false — throws if you run on web
);
Force a fresh token #
final token = await AppCheckSecure.getToken(forceRefresh: true);
// or
final token = await AppCheckSecure.refreshToken();
Error handling #
try {
await AppCheckSecure.initialize(config: config);
final token = await AppCheckSecure.getToken();
} on ConfigurationException catch (e) {
// Invalid config (e.g. missing webSiteKey)
} on InitializationException catch (e) {
// Not initialized or activation failed
} on TokenException catch (e) {
// Token fetch / refresh failed
} on PlatformNotSupportedException catch (e) {
// Not Android, iOS, or Web
} on AppCheckException catch (e) {
// Any package error
}
See the example/ app for a full demonstration.
Public API #
| API | Description |
|---|---|
AppCheckSecure.initialize(config:) |
Activate App Check |
AppCheckSecure.getToken({forceRefresh}) |
Get token (cached unless forced) |
AppCheckSecure.refreshToken() |
Force refresh |
AppCheckSecure.clearToken() |
Clear cache |
AppCheckSecure.currentToken |
Last cached token |
AppCheckSecure.isInitialized |
Initialization flag |
AppCheckSecure.onTokenChanged |
Token change stream |
AppCheckSecure.setAndroidDebugToken |
Set Android debug token (before init) |
AppCheckSecure.setIosDebugToken |
Set iOS debug token (before init) |
AppCheckSecure.setWebDebugToken |
Set Web debug token (before init) |
AppCheckSecure.getAndroidDebugToken |
Read Android debug token |
AppCheckSecure.getIosDebugToken |
Read iOS debug token |
AppCheckSecure.getWebDebugToken |
Read Web debug token |
AppCheckSecure.getDebugToken |
Read debug token for current platform |
AppCheckSecure.clearDebugTokens |
Clear setter-based debug tokens |
AppCheckSecure.dispose() |
Tear down resources |
Applications should not import or call firebase_app_check for day-to-day App Check usage when using this package.
Logging #
Enable with enableLogging: true:
[AppCheck] Initializing
[AppCheck] Android Provider : Play Integrity
[AppCheck] Initialization Completed
[AppCheck] Token Generated
[AppCheck] Token Refreshed
Troubleshooting #
InitializationException: AppCheckSecure is not initialized #
Call AppCheckSecure.initialize after Firebase.initializeApp() and before getToken / refreshToken.
ConfigurationException: webSiteKey is required #
On web, always provide a non-empty webSiteKey.
ConfigurationException about SafetyNet #
Use AndroidProviderType.playIntegrity instead. SafetyNet was removed from FlutterFire.
Token requests fail / 403 App attestation failed #
- Confirm the correct provider is registered in Firebase Console.
- For debug builds, register the debug token.
- On Android, verify SHA-256 fingerprints.
- On iOS, verify App Attest / DeviceCheck setup and provisioning.
- Prefer Monitoring mode until tokens succeed consistently.
Already initialized warning #
initialize is idempotent after the first success. Call dispose() if you need to re-initialize with a different config (e.g. tests).
Hot restart #
Call dispose() then initialize() again if state looks stale after hot restart.
FAQ #
Why not call Firebase App Check directly?
This package keeps provider selection, retries, caching, logging, and error mapping in one place so every app shares the same behavior.
Does this initialize Firebase?
No. Your app must call Firebase.initializeApp() first.
Is SafetyNet supported?
No longer by FlutterFire. The enum value remains and fails fast with a clear ConfigurationException.
Can I use this with multiple Firebase apps?
This package uses the default Firebase app (FirebaseAppCheck.instance). Multi-app support is out of scope by design.
Does clearToken deactivate App Check?
No. It only clears the in-memory cache. Use dispose() to tear down listeners and reset the service.
Is macOS / Windows / Linux supported?
No. Only Android, iOS, and Web. Other platforms throw PlatformNotSupportedException.
License #
See LICENSE.