diff --git a/docs/app-check/debug-provider.md b/docs/app-check/debug-provider.md index 44953a4f7cd6..a46ed7d107d2 100644 --- a/docs/app-check/debug-provider.md +++ b/docs/app-check/debug-provider.md @@ -37,8 +37,8 @@ To use the debug provider while running your app in a simulator interactively WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await FirebaseAppCheck.instance.activate( - // Set appleProvider to `AppleProvider.debug` - appleProvider: AppleProvider.debug, + // Set providerApple to use AppleDebugProvider + providerApple: AppleDebugProvider('123a4567-b89c-12d3-e456-789012345678'), ); runApp(App()); } @@ -83,9 +83,9 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); await FirebaseAppCheck.instance.activate( - webRecaptchaSiteKey: 'recaptcha-v3-site-key', - // Set androidProvider to `AndroidProvider.debug` - androidProvider: AndroidProvider.debug, + webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'), + // Set providerAndroid to use AndroidDebugProvider + providerAndroid: AndroidDebugProvider('123a4567-b89c-12d3-e456-789012345678'), ); runApp(App()); } @@ -146,3 +146,44 @@ Because this token allows access to your Firebase resources without a valid device, it is crucial that you keep it private. Don't commit it to a public repository, and if a registered token is ever compromised, revoke it immediately in the Firebase console. + +## Manually setting up the App Check Debug Token for CI environment or development + +If you want to use the debug provider in a testing environment or CI, you can +manually set the debug token in your app. This is useful when you want to run +your app in an environment where the debug token is not automatically generated. + +To manually set the debug token, pass your debug token directly to the debug provider +classes when activating App Check. For example: + +```dart +import 'package:flutter/material.dart'; +import 'package:firebase_core/firebase_core.dart'; + +// Import the firebase_app_check plugin +import 'package:firebase_app_check/firebase_app_check.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(); + await FirebaseAppCheck.instance.activate( + webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'), + // Set providerAndroid with debug token + providerAndroid: AndroidDebugProvider('123a4567-b89c-12d3-e456-789012345678'), + // Set providerApple with debug token + providerApple: AppleDebugProvider('123a4567-b89c-12d3-e456-789012345678'), + ); + runApp(App()); +} + +``` + +{# Google-internal common file: #} +<<../_includes/manage-debug-tokens.md>> + +After you register the token, Firebase backend services will accept it as valid. + +Because this token allows access to your Firebase resources without a +valid device, it is crucial that you keep it private. Don't commit it to a +public repository, and if a registered token is ever compromised, revoke it +immediately in the Firebase console. diff --git a/docs/app-check/default-providers.md b/docs/app-check/default-providers.md index 9d3b8da0cb23..bfa827e997e5 100644 --- a/docs/app-check/default-providers.md +++ b/docs/app-check/default-providers.md @@ -81,19 +81,19 @@ Future main() async { // You can also use a `ReCaptchaEnterpriseProvider` provider instance as an // argument for `webProvider` webProvider: ReCaptchaV3Provider('recaptcha-v3-site-key'), - // Default provider for Android is the Play Integrity provider. You can use the "AndroidProvider" enum to choose + // Default provider for Android is the Play Integrity provider. You can use the "providerAndroid" parameter to choose // your preferred provider. Choose from: - // 1. Debug provider - // 2. Safety Net provider - // 3. Play Integrity provider - androidProvider: AndroidProvider.debug, - // Default provider for iOS/macOS is the Device Check provider. You can use the "AppleProvider" enum to choose - // your preferred provider. Choose from: - // 1. Debug provider - // 2. Device Check provider - // 3. App Attest provider - // 4. App Attest provider with fallback to Device Check provider (App Attest provider is only available on iOS 14.0+, macOS 14.0+) - appleProvider: AppleProvider.appAttest, + // 1. AndroidDebugProvider for debug environments + // 2. AndroidSafetyNetProvider (will be deprecated in the future) + // 3. AndroidPlayIntegrityProvider + providerAndroid: AndroidDebugProvider(), + // Default provider for iOS/macOS is the Device Check provider. You can use the "providerApple" parameter to choose + // your preferred provider. Choose from: + // 1. AppleDebugProvider for debug environments + // 2. AppleDeviceCheckProvider + // 3. AppleAppAttestProvider + // 4. AppleAppAttestProviderWithDeviceCheckFallback (App Attest provider is only available on iOS 14.0+, macOS 14.0+) + providerApple: AppleAppAttestProvider(), ); runApp(App()); } diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java index b82275e556e2..6fec2cf81a91 100644 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppCheckPlugin.java @@ -102,6 +102,8 @@ private Task activate(Map arguments) { case debugProvider: { FirebaseAppCheck firebaseAppCheck = getAppCheck(arguments); + FlutterFirebaseAppRegistrar.debugToken = + (String) arguments.get("androidDebugToken"); firebaseAppCheck.installAppCheckProviderFactory( DebugAppCheckProviderFactory.getInstance()); break; diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java index 69aaee5288c5..2355ec819c0c 100644 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/java/io/flutter/plugins/firebase/appcheck/FlutterFirebaseAppRegistrar.java @@ -5,17 +5,38 @@ package io.flutter.plugins.firebase.appcheck; import androidx.annotation.Keep; +import androidx.annotation.Nullable; +import com.google.firebase.appcheck.debug.InternalDebugSecretProvider; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentRegistrar; import com.google.firebase.platforminfo.LibraryVersionComponent; -import java.util.Collections; +import java.util.Arrays; import java.util.List; @Keep -public class FlutterFirebaseAppRegistrar implements ComponentRegistrar { +public class FlutterFirebaseAppRegistrar + implements ComponentRegistrar, InternalDebugSecretProvider { + + private static final String DEBUG_SECRET_NAME = "fire-app-check-debug-secret"; + public static String debugToken; + @Override public List> getComponents() { - return Collections.>singletonList( - LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION)); + Component library = + LibraryVersionComponent.create(BuildConfig.LIBRARY_NAME, BuildConfig.LIBRARY_VERSION); + + Component debugSecretProvider = + Component.builder(InternalDebugSecretProvider.class) + .name(DEBUG_SECRET_NAME) + .factory(container -> this) + .build(); + + return Arrays.asList(library, debugSecretProvider); + } + + @Nullable + @Override + public String getDebugSecret() { + return debugToken; } } diff --git a/packages/firebase_app_check/firebase_app_check/example/lib/main.dart b/packages/firebase_app_check/firebase_app_check/example/lib/main.dart index ee5741b51ef0..49a96a0116b1 100644 --- a/packages/firebase_app_check/firebase_app_check/example/lib/main.dart +++ b/packages/firebase_app_check/firebase_app_check/example/lib/main.dart @@ -2,11 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:cloud_firestore/cloud_firestore.dart'; import 'firebase_options.dart'; @@ -26,6 +26,8 @@ Future main() async { androidProvider: AndroidProvider.debug, appleProvider: AppleProvider.debug, webProvider: ReCaptchaV3Provider(kWebRecaptchaSiteKey), + providerAndroid: const AndroidDebugProvider(debugToken: 'androidDebug'), + providerApple: const AppleDebugProvider(debugToken: 'appleDebug'), ); runApp(MyApp()); diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m index 09d966017e20..aacddaf48f2f 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProvider.m @@ -14,10 +14,18 @@ - (id)initWithApp:app { return self; } -- (void)configure:(FIRApp *)app providerName:(NSString *)providerName { +- (void)configure:(FIRApp *)app + providerName:(NSString *)providerName + debugToken:(NSString *)debugToken { if ([providerName isEqualToString:@"debug"]) { + if (debugToken != nil) { + // We have a debug token, so just need to stuff it in the environment and it will hook up + char *key = "FIRAAppCheckDebugToken", *value = (char *)[debugToken UTF8String]; + int overwrite = 1; + setenv(key, value, overwrite); + } FIRAppCheckDebugProvider *provider = [[FIRAppCheckDebugProvider alloc] initWithApp:app]; - NSLog(@"Firebase App Check Debug Token: %@", [provider localDebugToken]); + if (debugToken == nil) NSLog(@"Firebase App Check Debug Token: %@", [provider localDebugToken]); self.delegateProvider = provider; } diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m index f6566f299321..3eee91a08fc8 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTAppCheckProviderFactory.m @@ -25,13 +25,15 @@ @implementation FLTAppCheckProviderFactory self.providers[app.name] = [FLTAppCheckProvider new]; FLTAppCheckProvider *provider = self.providers[app.name]; // We set "deviceCheck" as this is currently what is default. Backward compatible. - [provider configure:app providerName:@"deviceCheck"]; + [provider configure:app providerName:@"deviceCheck" debugToken:nil]; } return self.providers[app.name]; } -- (void)configure:(FIRApp *)app providerName:(NSString *)providerName { +- (void)configure:(FIRApp *)app + providerName:(NSString *)providerName + debugToken:(NSString *)debugToken { if (self.providers == nil) { self.providers = [NSMutableDictionary new]; } @@ -41,7 +43,7 @@ - (void)configure:(FIRApp *)app providerName:(NSString *)providerName { } FLTAppCheckProvider *provider = self.providers[app.name]; - [provider configure:app providerName:providerName]; + [provider configure:app providerName:providerName debugToken:debugToken]; } @end diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m index 70337cd50ea3..290cf29c7f11 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FLTFirebaseAppCheckPlugin.m @@ -123,9 +123,10 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutter - (void)activate:(id)arguments withMethodCallResult:(FLTFirebaseMethodCallResult *)result { NSString *appNameDart = arguments[@"appName"]; NSString *providerName = arguments[@"appleProvider"]; + NSString *debugToken = arguments[@"appleDebugToken"]; FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appNameDart]; - [self->providerFactory configure:app providerName:providerName]; + [self->providerFactory configure:app providerName:providerName debugToken:debugToken]; result.success(nil); } diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h index 87797e53def0..da9efde18370 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProvider.h @@ -10,7 +10,9 @@ @property id delegateProvider; -- (void)configure:(FIRApp *)app providerName:(NSString *)providerName; +- (void)configure:(FIRApp *)app + providerName:(NSString *)providerName + debugToken:(NSString *)debugToken; - (id)initWithApp:(FIRApp *)app; diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h index b98d5a823626..8e5511ebea94 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/include/FLTAppCheckProviderFactory.h @@ -7,6 +7,8 @@ @property NSMutableDictionary *_Nullable providers; -- (void)configure:(FIRApp *_Nonnull)app providerName:(NSString *_Nonnull)providerName; +- (void)configure:(FIRApp *_Nonnull)app + providerName:(NSString *_Nonnull)providerName + debugToken:(NSString *_Nullable)debugToken; @end diff --git a/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart index 3b962a8e7398..7267a3311ef1 100644 --- a/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check/lib/firebase_app_check.dart @@ -10,7 +10,15 @@ import 'package:firebase_core_platform_interface/firebase_core_platform_interfac export 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart' show AndroidProvider, + AndroidAppCheckProvider, + AndroidDebugProvider, + AndroidPlayIntegrityProvider, + AndroidSafetyNetProvider, AppleProvider, + AppleAppCheckProvider, + AppleDebugProvider, + AppleDeviceCheckProvider, + AppleAppAttestWithDeviceCheckFallbackProvider, ReCaptchaEnterpriseProvider, ReCaptchaV3Provider; export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' diff --git a/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart index 812beb08ecd5..a2e397033abe 100644 --- a/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check/lib/src/firebase_app_check.dart @@ -47,24 +47,42 @@ class FirebaseAppCheck extends FirebasePluginPlatform { /// Activates the Firebase App Check service. /// - /// On web, provide the reCAPTCHA v3 Site Key which can be found in the - /// Firebase Console. + /// ## Platform Configuration /// - /// On Android, the default provider is "play integrity". If you wish to set the provider to "safety net" or "debug", you may set the `androidProvider` property using the `AndroidProvider` enum + /// **Web**: Provide the reCAPTCHA v3 Site Key using `webProvider`, which can be + /// found in the Firebase Console. /// - /// On iOS or macOS, the default provider is "device check". If you wish to set the provider to "app attest", "debug" or "app attest with fallback to device check" - /// ("app attest" is only available on iOS 14.0+, macOS 14.0+), you may set the `appleProvider` property using the `AppleProvider` enum + /// **Android**: The default provider is "play integrity". Use `providerAndroid` + /// to configure alternative providers such as "safety net", debug providers, or + /// custom implementations via `AndroidAppCheckProvider`. + /// + /// **iOS/macOS**: The default provider is "device check". Use `providerApple` + /// to configure alternative providers such as "app attest", debug providers, or + /// "app attest with fallback to device check" via `AppleAppCheckProvider`. + /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. + /// + /// ## Migration Notice + /// + /// The `androidProvider` and `appleProvider` parameters will be deprecated + /// in a future release. Use `providerAndroid` and `providerApple` instead, + /// which support the new provider classes including `AndroidDebugProvider` + /// and `AppleDebugProvider` for passing debug tokens directly. /// /// For more information, see [the Firebase Documentation](https://firebase.google.com/docs/app-check) Future activate({ WebProvider? webProvider, AndroidProvider androidProvider = AndroidProvider.playIntegrity, AppleProvider appleProvider = AppleProvider.deviceCheck, + AndroidAppCheckProvider providerAndroid = + const AndroidPlayIntegrityProvider(), + AppleAppCheckProvider providerApple = const AppleDeviceCheckProvider(), }) { return _delegate.activate( webProvider: webProvider, androidProvider: androidProvider, appleProvider: appleProvider, + providerAndroid: providerAndroid, + providerApple: providerApple, ); } diff --git a/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart b/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart index 0ebaf633a7aa..70dcb04f0a6c 100755 --- a/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart +++ b/packages/firebase_app_check/firebase_app_check/test/firebase_app_check_test.dart @@ -4,9 +4,10 @@ import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter_test/flutter_test.dart'; + import './mock.dart'; void main() { @@ -57,6 +58,12 @@ void main() { test('successful call', () async { await appCheck.activate( webProvider: ReCaptchaV3Provider('key'), + providerAndroid: const AndroidDebugProvider( + debugToken: 'androidDebug', + ), + providerApple: const AppleDebugProvider( + debugToken: 'appleDebug', + ), ); expect( @@ -66,8 +73,10 @@ void main() { 'FirebaseAppCheck#activate', arguments: { 'appName': defaultFirebaseAppName, - 'androidProvider': 'playIntegrity', - 'appleProvider': 'deviceCheck', + 'androidProvider': 'debug', + 'appleProvider': 'debug', + 'androidDebugToken': 'androidDebug', + 'appleDebugToken': 'appleDebug', }, ), ], diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart index 47d4fd50cd03..c8292c8b7eaf 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/firebase_app_check_platform_interface.dart @@ -3,8 +3,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -export 'src/method_channel/method_channel_firebase_app_check.dart'; -export 'src/platform_interface/platform_interface_firebase_app_check.dart'; export 'src/android_provider.dart'; +export 'src/android_providers.dart'; export 'src/apple_provider.dart'; +export 'src/apple_providers.dart'; +export 'src/method_channel/method_channel_firebase_app_check.dart'; +export 'src/platform_interface/platform_interface_firebase_app_check.dart'; export 'src/web_providers.dart'; diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart new file mode 100644 index 000000000000..058573a030a0 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/android_providers.dart @@ -0,0 +1,48 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +abstract class AndroidAppCheckProvider { + final String type; + const AndroidAppCheckProvider(this.type); +} + +/// Debug provider for Android. +/// +/// No further configuration in your Android code required. You simply need to +/// copy/paste the debug token from the console when you run the app and add the +/// token to your Firebase console. +/// +/// If you set the [debugToken] here, it will be used when activating the provider. +/// +/// See documentation: https://firebase.google.com/docs/app-check/android/debug-provider +class AndroidDebugProvider extends AndroidAppCheckProvider { + /// Creates an Android debug provider with an optional debug token. + /// + /// The [debugToken] can be set here or passed separately to the activate method. + const AndroidDebugProvider({this.debugToken}) : super('debug'); + + /// The debug token for this provider. + final String? debugToken; +} + +/// Play Integrity provider for Android (Firebase recommended). +/// +/// See documentation: https://firebase.google.com/docs/app-check/android/play-integrity-provider +class AndroidPlayIntegrityProvider extends AndroidAppCheckProvider { + const AndroidPlayIntegrityProvider() : super('playIntegrity'); +} + +/// Safety Net provider for Android. +/// +/// @deprecated Safety Net provider is deprecated and will be removed in a future release. +/// Play Integrity is the recommended provider. +/// +/// See documentation: https://firebase.google.com/docs/app-check/android/safetynet-provider +class AndroidSafetyNetProvider extends AndroidAppCheckProvider { + @Deprecated( + 'Safety Net provider is deprecated and will be removed in a future release. ' + 'Play Integrity is the recommended provider.', + ) + const AndroidSafetyNetProvider() : super('safetyNet'); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart new file mode 100644 index 000000000000..5e6af1ce2359 --- /dev/null +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/apple_providers.dart @@ -0,0 +1,56 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +abstract class AppleAppCheckProvider { + final String type; + const AppleAppCheckProvider(this.type); +} + +/// Debug provider for Apple platforms. +/// +/// No further configuration in your iOS code required. You simply need to +/// copy/paste the debug token from the console when you run the app and add the +/// token to your Firebase console. +/// +/// If you set the [debugToken] here, it will be used when activating the provider. +/// +/// See documentation: https://firebase.google.com/docs/app-check/ios/debug-provider +class AppleDebugProvider extends AppleAppCheckProvider { + /// Creates an Apple debug provider with an optional debug token. + /// + /// The [debugToken] can be set here or passed separately to the activate method. + /// You have to re-run app after changing debug token. + const AppleDebugProvider({this.debugToken}) : super('debug'); + + /// The debug token for this provider. + final String? debugToken; +} + +/// Device Check provider for Apple platforms. +/// +/// The default provider for iOS and macOS. +/// +/// See documentation: https://firebase.google.com/docs/app-check/ios/devicecheck-provider +class AppleDeviceCheckProvider extends AppleAppCheckProvider { + const AppleDeviceCheckProvider() : super('deviceCheck'); +} + +/// App Attest provider for Apple platforms (Firebase recommended). +/// +/// Only available on iOS 14.0+, macOS 14.0+. +/// +/// See documentation: https://firebase.google.com/docs/app-check/ios/app-attest-provider +class AppleAppAttestProvider extends AppleAppCheckProvider { + const AppleAppAttestProvider() : super('appAttest'); +} + +/// App Attest provider with Device Check fallback for Apple platforms. +/// +/// App Attest provider is only available on iOS 14.0+, macOS 14.0+ so this will +/// fall back to Device Check provider if App Attest provider is not available. +class AppleAppAttestWithDeviceCheckFallbackProvider + extends AppleAppCheckProvider { + const AppleAppAttestWithDeviceCheckFallbackProvider() + : super('appAttestWithDeviceCheckFallback'); +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart index edee4fbe09d0..59f05fa8b527 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/method_channel_firebase_app_check.dart @@ -76,19 +76,41 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { @override Future activate({ WebProvider? webProvider, + @Deprecated( + 'Use providerAndroid instead. ' + 'This parameter will be removed in a future major release.', + ) AndroidProvider? androidProvider, + @Deprecated( + 'Use providerApple instead. ' + 'This parameter will be removed in a future major release.', + ) AppleProvider? appleProvider, + AndroidAppCheckProvider? providerAndroid, + AppleAppCheckProvider? providerApple, }) async { try { await channel.invokeMethod('FirebaseAppCheck#activate', { 'appName': app.name, // Allow value to pass for debug mode for unit testing if (defaultTargetPlatform == TargetPlatform.android || kDebugMode) - 'androidProvider': getAndroidProviderString(androidProvider), + 'androidProvider': getAndroidProviderString( + legacyProvider: androidProvider, + newProvider: providerAndroid, + ), + if (providerAndroid is AndroidDebugProvider && + providerAndroid.debugToken != null) + 'androidDebugToken': providerAndroid.debugToken, if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS || kDebugMode) - 'appleProvider': getAppleProviderString(appleProvider), + 'appleProvider': getAppleProviderString( + legacyProvider: appleProvider, + newProvider: providerApple, + ), + if (providerApple is AppleDebugProvider && + providerApple.debugToken != null) + 'appleDebugToken': providerApple.debugToken, }); } on PlatformException catch (e, s) { convertPlatformException(e, s); diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/utils/provider_to_string.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/utils/provider_to_string.dart index 8f34852330a1..2248ce263528 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/utils/provider_to_string.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/method_channel/utils/provider_to_string.dart @@ -3,10 +3,38 @@ // BSD-style license that can be found in the LICENSE file. import 'package:firebase_app_check_platform_interface/src/android_provider.dart'; +import 'package:firebase_app_check_platform_interface/src/android_providers.dart'; import 'package:firebase_app_check_platform_interface/src/apple_provider.dart'; +import 'package:firebase_app_check_platform_interface/src/apple_providers.dart'; -/// Converts [AndroidProvider] to [String] -String getAndroidProviderString(AndroidProvider? provider) { +/// Converts [AndroidAppCheckProvider] to [String] with backwards compatibility +String getAndroidProviderString({ + AndroidProvider? legacyProvider, + AndroidAppCheckProvider? newProvider, +}) { + // Prefer new provider over legacy provider + if (newProvider != null) { + return newProvider.type; + } + + return getLegacyAndroidProviderString(legacyProvider); +} + +/// Converts [AppleAppCheckProvider] to [String] with backwards compatibility +String getAppleProviderString({ + AppleProvider? legacyProvider, + AppleAppCheckProvider? newProvider, +}) { + // Prefer new provider over legacy provider + if (newProvider != null) { + return newProvider.type; + } + + return getLegacyAppleProviderString(legacyProvider); +} + +/// Converts [AndroidProvider] enum to [String] +String getLegacyAndroidProviderString(AndroidProvider? provider) { switch (provider) { // ignore: deprecated_member_use_from_same_package case AndroidProvider.safetyNet: @@ -19,8 +47,8 @@ String getAndroidProviderString(AndroidProvider? provider) { } } -/// Converts [AppleProvider] to [String] -String getAppleProviderString(AppleProvider? provider) { +/// Converts [AppleProvider] enum to [String] +String getLegacyAppleProviderString(AppleProvider? provider) { switch (provider) { case AppleProvider.debug: return 'debug'; diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart index a1b1e66826cb..988d7e0b8e1f 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/platform_interface/platform_interface_firebase_app_check.dart @@ -53,19 +53,42 @@ abstract class FirebaseAppCheckPlatform extends PlatformInterface { /// Activates the Firebase App Check service. /// - /// On web, provide the reCAPTCHA v3 Site Key which can be found in the - /// Firebase Console. + /// ## Platform Configuration /// - /// On Android, the default provider is "play integrity". If you wish to set the provider to "safety net" or "debug", you may set the `androidProvider` property using the `AndroidProvider` enum + /// **Web**: Provide the reCAPTCHA v3 Site Key using `webProvider`, which can be + /// found in the Firebase Console. /// - /// On iOS or macOS, the default provider is "device check". If you wish to set the provider to "app attest", "debug" or "app attest with fallback to device check" - /// ("app attest" is only available on iOS 14.0+, macOS 14.0+), you may set the `appleProvider` property using the `AppleProvider` enum + /// **Android**: The default provider is "play integrity". Use `providerAndroid` + /// to configure alternative providers such as "safety net", debug providers, or + /// custom implementations via `AndroidAppCheckProvider`. + /// + /// **iOS/macOS**: The default provider is "device check". Use `providerApple` + /// to configure alternative providers such as "app attest", debug providers, or + /// "app attest with fallback to device check" via `AppleAppCheckProvider`. + /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. + /// + /// ## Migration Notice + /// + /// The `androidProvider` and `appleProvider` parameters will be deprecated + /// in a future release. Use `providerAndroid` and `providerApple` instead, + /// which support the new provider classes including `AndroidDebugProvider` + /// and `AppleDebugProvider` for passing debug tokens directly. /// /// For more information, see [the Firebase Documentation](https://firebase.google.com/docs/app-check) Future activate({ WebProvider? webProvider, + @Deprecated( + 'Use providerAndroid instead. ' + 'This parameter will be removed in a future major release.', + ) AndroidProvider? androidProvider, + @Deprecated( + 'Use providerApple instead. ' + 'This parameter will be removed in a future major release.', + ) AppleProvider? appleProvider, + AndroidAppCheckProvider? providerAndroid, + AppleAppCheckProvider? providerApple, }) { throw UnimplementedError('activate() is not implemented'); } diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart index 7b1082aa299b..ef861082a228 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/test/method_channel_tests/method_channel_firebase_app_check_test.dart @@ -69,6 +69,8 @@ void main() { test('activate', () async { await appCheck.activate( webProvider: ReCaptchaV3Provider('test-key'), + providerAndroid: AndroidPlayIntegrityProvider(), + providerApple: AppleDeviceCheckProvider(), ); expect( methodCallLogger, @@ -85,6 +87,29 @@ void main() { ); }); + test('activate with debug providers', () async { + await appCheck.activate( + webProvider: ReCaptchaV3Provider('test-key'), + providerAndroid: AndroidDebugProvider(debugToken: 'androidDebug'), + providerApple: AppleDebugProvider(debugToken: 'appleDebug'), + ); + expect( + methodCallLogger, + [ + isMethodCall( + 'FirebaseAppCheck#activate', + arguments: { + 'appName': defaultFirebaseAppName, + 'androidProvider': 'debug', + 'appleProvider': 'debug', + 'androidDebugToken': 'androidDebug', + 'appleDebugToken': 'appleDebug', + }, + ), + ], + ); + }); + test('getToken', () async { final tokenResult = await appCheck.getToken(true); expect( diff --git a/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart b/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart index 4fc12a252dee..ca4d5aa949c8 100644 --- a/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart +++ b/packages/firebase_app_check/firebase_app_check_web/lib/firebase_app_check_web.dart @@ -97,8 +97,18 @@ class FirebaseAppCheckWeb extends FirebaseAppCheckPlatform { @override Future activate({ WebProvider? webProvider, + @Deprecated( + 'Use providerAndroid instead. ' + 'This parameter will be removed in a future major release.', + ) AndroidProvider? androidProvider, + @Deprecated( + 'Use providerApple instead. ' + 'This parameter will be removed in a future major release.', + ) AppleProvider? appleProvider, + AndroidAppCheckProvider? providerAndroid, + AppleAppCheckProvider? providerApple, }) async { // save the recaptcha type and site key for future startups if (webProvider != null) { diff --git a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart index 617475de2f14..e571c1b5b1bf 100644 --- a/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart +++ b/packages/firebase_app_check/firebase_app_check_web/test/firebase_app_check_web_test.mocks.dart @@ -1,7 +1,4 @@ -// Copyright 2021 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// Mocks generated by Mockito 5.4.0 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in firebase_app_check_web/test/firebase_app_check_web_test.dart. // Do not manually edit this file. @@ -13,13 +10,17 @@ import 'package:firebase_app_check_platform_interface/firebase_app_check_platfor import 'package:firebase_app_check_web/firebase_app_check_web.dart' as _i4; import 'package:firebase_core/firebase_core.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters // ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -68,6 +69,7 @@ class MockFirebaseAppCheckWeb extends _i1.Mock returnValue: _i5.Stream.empty(), returnValueForMissingStub: _i5.Stream.empty(), ) as _i5.Stream); + @override _i2.FirebaseApp get app => (super.noSuchMethod( Invocation.getter(#app), @@ -80,6 +82,7 @@ class MockFirebaseAppCheckWeb extends _i1.Mock Invocation.getter(#app), ), ) as _i2.FirebaseApp); + @override _i3.FirebaseAppCheckPlatform delegateFor({required _i2.FirebaseApp? app}) => (super.noSuchMethod( @@ -105,6 +108,7 @@ class MockFirebaseAppCheckWeb extends _i1.Mock ), ), ) as _i3.FirebaseAppCheckPlatform); + @override _i4.FirebaseAppCheckWeb setInitialValues() => (super.noSuchMethod( Invocation.method( @@ -126,11 +130,14 @@ class MockFirebaseAppCheckWeb extends _i1.Mock ), ), ) as _i4.FirebaseAppCheckWeb); + @override _i5.Future activate({ _i3.WebProvider? webProvider, _i3.AndroidProvider? androidProvider, _i3.AppleProvider? appleProvider, + _i3.AndroidAppCheckProvider? providerAndroid, + _i3.AppleAppCheckProvider? providerApple, }) => (super.noSuchMethod( Invocation.method( @@ -140,11 +147,14 @@ class MockFirebaseAppCheckWeb extends _i1.Mock #webProvider: webProvider, #androidProvider: androidProvider, #appleProvider: appleProvider, + #providerAndroid: providerAndroid, + #providerApple: providerApple, }, ), returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + @override _i5.Future getToken(bool? forceRefresh) => (super.noSuchMethod( Invocation.method( @@ -154,6 +164,30 @@ class MockFirebaseAppCheckWeb extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + + @override + _i5.Future getLimitedUseToken() => (super.noSuchMethod( + Invocation.method( + #getLimitedUseToken, + [], + ), + returnValue: _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getLimitedUseToken, + [], + ), + )), + returnValueForMissingStub: + _i5.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getLimitedUseToken, + [], + ), + )), + ) as _i5.Future); + @override _i5.Future setTokenAutoRefreshEnabled( bool? isTokenAutoRefreshEnabled) => diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart index cb633caecf6f..86b7a191caaf 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.mocks.dart @@ -1,18 +1,4 @@ -// Copyright 2024 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Mocks generated by Mockito 5.4.4 from annotations +// Mocks generated by Mockito 5.4.6 from annotations // in firebase_data_connect/test/src/network/rest_transport_test.dart. // Do not manually edit this file. @@ -40,6 +26,7 @@ import 'package:mockito/src/dummies.dart' as _i8; // ignore_for_file: deprecated_member_use_from_same_package // ignore_for_file: implementation_imports // ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable // ignore_for_file: prefer_const_constructors // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types @@ -755,6 +742,12 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { ), ) as _i5.FirebaseApp); + @override + _i6.Stream get onTokenChange => (super.noSuchMethod( + Invocation.getter(#onTokenChange), + returnValue: _i6.Stream.empty(), + ) as _i6.Stream); + @override set app(_i5.FirebaseApp? _app) => super.noSuchMethod( Invocation.setter( @@ -764,12 +757,6 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { returnValueForMissingStub: null, ); - @override - _i6.Stream get onTokenChange => (super.noSuchMethod( - Invocation.getter(#onTokenChange), - returnValue: _i6.Stream.empty(), - ) as _i6.Stream); - @override Map get pluginConstants => (super.noSuchMethod( Invocation.getter(#pluginConstants), @@ -781,6 +768,10 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { _i11.WebProvider? webProvider, _i11.AndroidProvider? androidProvider = _i11.AndroidProvider.playIntegrity, _i11.AppleProvider? appleProvider = _i11.AppleProvider.deviceCheck, + _i11.AndroidAppCheckProvider? providerAndroid = + const _i11.AndroidPlayIntegrityProvider(), + _i11.AppleAppCheckProvider? providerApple = + const _i11.AppleDeviceCheckProvider(), }) => (super.noSuchMethod( Invocation.method( @@ -790,6 +781,8 @@ class MockFirebaseAppCheck extends _i1.Mock implements _i10.FirebaseAppCheck { #webProvider: webProvider, #androidProvider: androidProvider, #appleProvider: appleProvider, + #providerAndroid: providerAndroid, + #providerApple: providerApple, }, ), returnValue: _i6.Future.value(), diff --git a/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart b/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart index ca88b3262e5c..dbe3738fad47 100644 --- a/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart +++ b/tests/integration_test/firebase_app_check/firebase_app_check_e2e_test.dart @@ -77,6 +77,38 @@ void main() { }, skip: kIsWeb, ); + + test( + 'debugToken on Android', + () async { + await expectLater( + FirebaseAppCheck.instance.activate( + androidProvider: AndroidProvider.debug, + providerAndroid: const AndroidDebugProvider( + debugToken: 'debug_token', + ), + ), + completes, + ); + }, + skip: defaultTargetPlatform != TargetPlatform.android, + ); + + test( + 'debugToken on iOS', + () async { + await expectLater( + FirebaseAppCheck.instance.activate( + appleProvider: AppleProvider.debug, + providerApple: const AppleDebugProvider( + debugToken: 'debug_token', + ), + ), + completes, + ); + }, + skip: defaultTargetPlatform != TargetPlatform.iOS, + ); }, ); }