diff --git a/packages/firebase_app_check/firebase_app_check/android/build.gradle b/packages/firebase_app_check/firebase_app_check/android/build.gradle index 45c2fa4d6345..81e347abd98e 100644 --- a/packages/firebase_app_check/firebase_app_check/android/build.gradle +++ b/packages/firebase_app_check/firebase_app_check/android/build.gradle @@ -80,6 +80,7 @@ android { implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}") implementation 'com.google.firebase:firebase-appcheck-debug' implementation 'com.google.firebase:firebase-appcheck-playintegrity' + implementation 'com.google.firebase:firebase-appcheck-recaptchaenterprise:16.0.0-beta01' implementation 'androidx.annotation:annotation:1.7.0' } } diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt index 0dc0ce26dc7e..4bb063508300 100644 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/FirebaseAppCheckPlugin.kt @@ -9,6 +9,7 @@ import com.google.firebase.FirebaseApp import com.google.firebase.appcheck.FirebaseAppCheck import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory +import com.google.firebase.appcheck.recaptchaenterprise.RecaptchaEnterpriseAppCheckProviderFactory import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding import io.flutter.plugin.common.BinaryMessenger @@ -55,6 +56,7 @@ class FirebaseAppCheckPlugin : androidProvider: String?, appleProvider: String?, debugToken: String?, + recaptchaEnterpriseSiteKey: String?, callback: (Result) -> Unit ) { try { @@ -66,6 +68,16 @@ class FirebaseAppCheckPlugin : DebugAppCheckProviderFactory.getInstance() ) } + "recaptchaEnterprise" -> { + if (recaptchaEnterpriseSiteKey != null) { + firebaseAppCheck.installAppCheckProviderFactory( + RecaptchaEnterpriseAppCheckProviderFactory.getInstance(recaptchaEnterpriseSiteKey) + ) + } else { + callback(Result.failure(FlutterError("invalid-argument", "Site key is required for reCAPTCHA Enterprise", null))) + return + } + } else -> { firebaseAppCheck.installAppCheckProviderFactory( PlayIntegrityAppCheckProviderFactory.getInstance() diff --git a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt index 0a046dd5aa47..099f8514464d 100644 --- a/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt +++ b/packages/firebase_app_check/firebase_app_check/android/src/main/kotlin/io/flutter/plugins/firebase/appcheck/GeneratedAndroidFirebaseAppCheck.g.kt @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon @file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") @@ -49,7 +49,7 @@ class FlutterError ( val code: String, override val message: String? = null, val details: Any? = null -) : Throwable() +) : RuntimeException() private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return super.readValueOfType(type, buffer) @@ -62,7 +62,7 @@ private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessage /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ interface FirebaseAppCheckHostApi { - fun activate(appName: String, androidProvider: String?, appleProvider: String?, debugToken: String?, callback: (Result) -> Unit) + fun activate(appName: String, androidProvider: String?, appleProvider: String?, debugToken: String?, recaptchaEnterpriseSiteKey: String?, callback: (Result) -> Unit) fun getToken(appName: String, forceRefresh: Boolean, callback: (Result) -> Unit) fun setTokenAutoRefreshEnabled(appName: String, isTokenAutoRefreshEnabled: Boolean, callback: (Result) -> Unit) fun registerTokenListener(appName: String, callback: (Result) -> Unit) @@ -86,7 +86,8 @@ interface FirebaseAppCheckHostApi { val androidProviderArg = args[1] as String? val appleProviderArg = args[2] as String? val debugTokenArg = args[3] as String? - api.activate(appNameArg, androidProviderArg, appleProviderArg, debugTokenArg) { result: Result -> + val recaptchaEnterpriseSiteKeyArg = args[4] as String? + api.activate(appNameArg, androidProviderArg, appleProviderArg, debugTokenArg, recaptchaEnterpriseSiteKeyArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) diff --git a/packages/firebase_app_check/firebase_app_check/example/android/build.gradle b/packages/firebase_app_check/firebase_app_check/example/android/build.gradle index d2ffbffa4cd2..c63004eaacea 100644 --- a/packages/firebase_app_check/firebase_app_check/example/android/build.gradle +++ b/packages/firebase_app_check/firebase_app_check/example/android/build.gradle @@ -1,5 +1,6 @@ allprojects { repositories { + mavenLocal() google() mavenCentral() } diff --git a/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle b/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle index 30463c1cf2f2..4fb566e9929e 100644 --- a/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle +++ b/packages/firebase_app_check/firebase_app_check/example/android/settings.gradle @@ -22,7 +22,7 @@ plugins { // START: FlutterFire Configuration id "com.google.gms.google-services" version "4.3.15" apply false // END: FlutterFire Configuration - id "org.jetbrains.kotlin.android" version "1.9.22" apply false + id "org.jetbrains.kotlin.android" version "2.1.0" apply false } include ":app" 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 8f26c6439a83..8045706d9add 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 @@ -13,6 +13,8 @@ import 'package:flutter/material.dart'; import 'firebase_options.dart'; const kWebRecaptchaSiteKey = '6Lemcn0dAAAAABLkf6aiiHvpGD6x-zF3nOSDU2M8'; +const kAndroidRecaptchaSiteKey = '6LfQpf4sAAAAAJnxnEmtVMpjkwuLpDZnxxcLbjxb'; +const kAppleRecaptchaSiteKey = '6LeJev4sAAAAADbZGhkk_kkxSEzxRjzzZRC4R1R-'; // Windows: create a debug token in the Firebase Console // (App Check > Apps > Manage debug tokens), then paste it here @@ -79,13 +81,29 @@ class _FirebaseAppCheck extends State { final appCheck = FirebaseAppCheck.instance; String _message = ''; String _eventToken = 'not yet'; + late final TextEditingController _webSiteKeyController; + late final TextEditingController _androidSiteKeyController; + late final TextEditingController _appleSiteKeyController; @override void initState() { + _webSiteKeyController = TextEditingController(text: kWebRecaptchaSiteKey); + _androidSiteKeyController = + TextEditingController(text: kAndroidRecaptchaSiteKey); + _appleSiteKeyController = + TextEditingController(text: kAppleRecaptchaSiteKey); appCheck.onTokenChange.listen(setEventToken); super.initState(); } + @override + void dispose() { + _webSiteKeyController.dispose(); + _androidSiteKeyController.dispose(); + _appleSiteKeyController.dispose(); + super.dispose(); + } + void setMessage(String message) { setState(() { _message = message; @@ -101,16 +119,25 @@ class _FirebaseAppCheck extends State { Future _activate({ AndroidAppCheckProvider? android, AppleAppCheckProvider? apple, + String? webProviderType, WindowsAppCheckProvider? windows, }) async { try { + dynamic providerWeb; + if (webProviderType == 'enterprise') { + providerWeb = ReCaptchaEnterpriseProvider(_webSiteKeyController.text); + } else if (webProviderType == 'v3') { + providerWeb = ReCaptchaV3Provider(_webSiteKeyController.text); + } + await appCheck.activate( providerAndroid: android ?? const AndroidPlayIntegrityProvider(), providerApple: apple ?? const AppleDeviceCheckProvider(), - providerWeb: ReCaptchaV3Provider(kWebRecaptchaSiteKey), + providerWeb: providerWeb, providerWindows: windows ?? const WindowsDebugProvider(), ); final providerName = windows?.runtimeType.toString() ?? + webProviderType ?? apple?.runtimeType.toString() ?? android?.runtimeType.toString() ?? 'default'; @@ -136,40 +163,121 @@ class _FirebaseAppCheck extends State { style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - ElevatedButton( - onPressed: () => _activate( - android: const AndroidDebugProvider(), - apple: const AppleDebugProvider(), - windows: WindowsDebugProvider( - debugToken: - kWindowsDebugToken.isNotEmpty ? kWindowsDebugToken : null, + if (kIsWeb) ...[ + ElevatedButton( + onPressed: () => _activate( + webProviderType: 'v3', ), + child: const Text('activate(Web reCAPTCHA v3)'), ), - child: const Text('activate(Debug)'), - ), - ElevatedButton( - onPressed: () => _activate( - android: const AndroidPlayIntegrityProvider(), - apple: const AppleDeviceCheckProvider(), + const SizedBox(height: 8), + TextField( + controller: _webSiteKeyController, + decoration: const InputDecoration( + labelText: 'Web reCAPTCHA Site Key', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: () => _activate( + webProviderType: 'enterprise', + ), + child: const Text('activate(Web reCAPTCHA Enterprise)'), + ), + ], + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) ...[ + ElevatedButton( + onPressed: () => _activate( + android: const AndroidDebugProvider(), + ), + child: const Text('activate(Android Debug)'), + ), + ElevatedButton( + onPressed: () => _activate( + android: const AndroidPlayIntegrityProvider(), + ), + child: const Text('activate(Android Play Integrity)'), + ), + const SizedBox(height: 8), + TextField( + controller: _androidSiteKeyController, + decoration: const InputDecoration( + labelText: 'Android reCAPTCHA Site Key', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: () => _activate( + android: AndroidReCaptchaEnterpriseProvider( + siteKey: _androidSiteKeyController.text, + ), + ), + child: const Text('activate(Android reCAPTCHA Enterprise)'), + ), + ], + if (!kIsWeb && + (defaultTargetPlatform == TargetPlatform.iOS || + defaultTargetPlatform == TargetPlatform.macOS)) ...[ + ElevatedButton( + onPressed: () => _activate( + apple: const AppleDebugProvider(), + ), + child: const Text('activate(Apple Debug)'), + ), + ElevatedButton( + onPressed: () => _activate( + apple: const AppleDeviceCheckProvider(), + ), + child: const Text('activate(Apple DeviceCheck)'), ), - child: const Text('activate(PlayIntegrity / DeviceCheck)'), - ), - if (!kIsWeb) ElevatedButton( onPressed: () => _activate( apple: const AppleAppAttestProvider(), ), - child: const Text('activate(AppAttest)'), + child: const Text('activate(Apple AppAttest)'), ), - if (!kIsWeb) ElevatedButton( onPressed: () => _activate( apple: const AppleAppAttestWithDeviceCheckFallbackProvider(), ), child: const Text( - 'activate(AppAttest + DeviceCheck fallback)', + 'activate(Apple AppAttest + DeviceCheck fallback)', + ), + ), + if (defaultTargetPlatform == TargetPlatform.iOS) ...[ + const SizedBox(height: 8), + TextField( + controller: _appleSiteKeyController, + decoration: const InputDecoration( + labelText: 'Apple reCAPTCHA Site Key', + border: OutlineInputBorder(), + ), + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: () => _activate( + apple: AppleReCaptchaEnterpriseProvider( + siteKey: _appleSiteKeyController.text, + ), + ), + child: const Text('activate(Apple reCAPTCHA Enterprise)'), + ), + ], + ], + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) ...[ + ElevatedButton( + onPressed: () => _activate( + windows: WindowsDebugProvider( + debugToken: kWindowsDebugToken.isNotEmpty + ? kWindowsDebugToken + : null, + ), ), + child: const Text('activate(Windows Debug)'), ), + ], const SizedBox(height: 16), const Text( 'Actions', @@ -226,7 +334,7 @@ class _FirebaseAppCheck extends State { child: const Text('Test Firestore with App Check'), ), const SizedBox(height: 20), - Text( + SelectableText( _message, style: const TextStyle( color: Color.fromRGBO(47, 79, 79, 1), @@ -234,7 +342,7 @@ class _FirebaseAppCheck extends State { ), ), const SizedBox(height: 20), - Text( + SelectableText( 'Token from onTokenChange: $_eventToken', style: const TextStyle( color: Color.fromRGBO(128, 0, 128, 1), diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift index 1842cfe9c240..bc76bee940f7 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckMessages.g.swift @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -27,12 +27,13 @@ final class PigeonError: Error { } var localizedDescription: String { - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" } } private func wrapResult(_ result: Any?) -> [Any?] { - [result] + return [result] } private func wrapError(_ error: Any) -> [Any?] { @@ -52,13 +53,13 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } private func isNullish(_ value: Any?) -> Bool { - value is NSNull || value == nil + return value is NSNull || value == nil } private func nilOrValue(_ value: Any?) -> T? { @@ -66,73 +67,57 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } -private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader {} -private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter {} +private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader { +} + +private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter { +} private class FirebaseAppCheckMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - FirebaseAppCheckMessagesPigeonCodecReader(data: data) + return FirebaseAppCheckMessagesPigeonCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - FirebaseAppCheckMessagesPigeonCodecWriter(data: data) + return FirebaseAppCheckMessagesPigeonCodecWriter(data: data) } } class FirebaseAppCheckMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { - static let shared = - FirebaseAppCheckMessagesPigeonCodec( - readerWriter: FirebaseAppCheckMessagesPigeonCodecReaderWriter() - ) + static let shared = FirebaseAppCheckMessagesPigeonCodec(readerWriter: FirebaseAppCheckMessagesPigeonCodecReaderWriter()) } + /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol FirebaseAppCheckHostApi { - func activate(appName: String, androidProvider: String?, appleProvider: String?, - debugToken: String?, completion: @escaping (Result) -> Void) - func getToken(appName: String, forceRefresh: Bool, - completion: @escaping (Result) -> Void) - func setTokenAutoRefreshEnabled(appName: String, isTokenAutoRefreshEnabled: Bool, - completion: @escaping (Result) -> Void) + func activate(appName: String, androidProvider: String?, appleProvider: String?, debugToken: String?, recaptchaEnterpriseSiteKey: String?, completion: @escaping (Result) -> Void) + func getToken(appName: String, forceRefresh: Bool, completion: @escaping (Result) -> Void) + func setTokenAutoRefreshEnabled(appName: String, isTokenAutoRefreshEnabled: Bool, completion: @escaping (Result) -> Void) func registerTokenListener(appName: String, completion: @escaping (Result) -> Void) - func getLimitedUseAppCheckToken(appName: String, - completion: @escaping (Result) -> Void) + func getLimitedUseAppCheckToken(appName: String, completion: @escaping (Result) -> Void) } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. class FirebaseAppCheckHostApiSetup { - static var codec: FlutterStandardMessageCodec { - FirebaseAppCheckMessagesPigeonCodec.shared - } - - /// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the - /// `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppCheckHostApi?, - messageChannelSuffix: String = "") { + static var codec: FlutterStandardMessageCodec { FirebaseAppCheckMessagesPigeonCodec.shared } + /// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: FirebaseAppCheckHostApi?, messageChannelSuffix: String = "") { let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" - let activateChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate\(channelSuffix)", - binaryMessenger: binaryMessenger, - codec: codec - ) - if let api { + let activateChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { activateChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String let androidProviderArg: String? = nilOrValue(args[1]) let appleProviderArg: String? = nilOrValue(args[2]) let debugTokenArg: String? = nilOrValue(args[3]) - api.activate( - appName: appNameArg, - androidProvider: androidProviderArg, - appleProvider: appleProviderArg, - debugToken: debugTokenArg - ) { result in + let recaptchaEnterpriseSiteKeyArg: String? = nilOrValue(args[4]) + api.activate(appName: appNameArg, androidProvider: androidProviderArg, appleProvider: appleProviderArg, debugToken: debugTokenArg, recaptchaEnterpriseSiteKey: recaptchaEnterpriseSiteKeyArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -140,21 +125,17 @@ class FirebaseAppCheckHostApiSetup { } else { activateChannel.setMessageHandler(nil) } - let getTokenChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken\(channelSuffix)", - binaryMessenger: binaryMessenger, - codec: codec - ) - if let api { + let getTokenChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { getTokenChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String let forceRefreshArg = args[1] as! Bool api.getToken(appName: appNameArg, forceRefresh: forceRefreshArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -162,24 +143,17 @@ class FirebaseAppCheckHostApiSetup { } else { getTokenChannel.setMessageHandler(nil) } - let setTokenAutoRefreshEnabledChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled\(channelSuffix)", - binaryMessenger: binaryMessenger, - codec: codec - ) - if let api { + let setTokenAutoRefreshEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { setTokenAutoRefreshEnabledChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String let isTokenAutoRefreshEnabledArg = args[1] as! Bool - api.setTokenAutoRefreshEnabled( - appName: appNameArg, - isTokenAutoRefreshEnabled: isTokenAutoRefreshEnabledArg - ) { result in + api.setTokenAutoRefreshEnabled(appName: appNameArg, isTokenAutoRefreshEnabled: isTokenAutoRefreshEnabledArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -187,20 +161,16 @@ class FirebaseAppCheckHostApiSetup { } else { setTokenAutoRefreshEnabledChannel.setMessageHandler(nil) } - let registerTokenListenerChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener\(channelSuffix)", - binaryMessenger: binaryMessenger, - codec: codec - ) - if let api { + let registerTokenListenerChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { registerTokenListenerChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String api.registerTokenListener(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -208,20 +178,16 @@ class FirebaseAppCheckHostApiSetup { } else { registerTokenListenerChannel.setMessageHandler(nil) } - let getLimitedUseAppCheckTokenChannel = FlutterBasicMessageChannel( - name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken\(channelSuffix)", - binaryMessenger: binaryMessenger, - codec: codec - ) - if let api { + let getLimitedUseAppCheckTokenChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { getLimitedUseAppCheckTokenChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String api.getLimitedUseAppCheckToken(appName: appNameArg) { result in switch result { - case let .success(res): + case .success(let res): reply(wrapResult(res)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } diff --git a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift index 5aec32b1ad2c..7b9089edaba2 100644 --- a/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift +++ b/packages/firebase_app_check/firebase_app_check/ios/firebase_app_check/Sources/firebase_app_check/FirebaseAppCheckPlugin.swift @@ -60,7 +60,7 @@ public class FirebaseAppCheckPlugin: NSObject, FlutterPlugin, private var binaryMessenger: FlutterBinaryMessenger? func activate(appName: String, androidProvider: String?, appleProvider: String?, - debugToken: String?, + debugToken: String?, recaptchaEnterpriseSiteKey: String?, completion: @escaping (Result) -> Void) { guard let app = FLTFirebasePlugin.firebaseAppNamed(appName) else { completion(.failure(FlutterError( @@ -70,7 +70,12 @@ public class FirebaseAppCheckPlugin: NSObject, FlutterPlugin, } let provider = appleProvider ?? "deviceCheck" - providerFactory?.configure(app: app, providerName: provider, debugToken: debugToken) + providerFactory?.configure( + app: app, + providerName: provider, + debugToken: debugToken, + recaptchaEnterpriseSiteKey: recaptchaEnterpriseSiteKey + ) completion(.success(())) } @@ -242,24 +247,44 @@ class FlutterAppCheckProviderFactory: NSObject, AppCheckProviderFactory { if providers[app.name] == nil { let wrapper = AppCheckProviderWrapper() // Default to deviceCheck. activate() will reconfigure with the correct provider. - wrapper.configure(app: app, providerName: "deviceCheck", debugToken: nil) + wrapper.configure( + app: app, + providerName: "deviceCheck", + debugToken: nil, + recaptchaEnterpriseSiteKey: nil + ) providers[app.name] = wrapper } return providers[app.name] } - func configure(app: FirebaseApp, providerName: String, debugToken: String?) { + func configure( + app: FirebaseApp, + providerName: String, + debugToken: String?, + recaptchaEnterpriseSiteKey: String? + ) { if providers[app.name] == nil { providers[app.name] = AppCheckProviderWrapper() } - providers[app.name]?.configure(app: app, providerName: providerName, debugToken: debugToken) + providers[app.name]?.configure( + app: app, + providerName: providerName, + debugToken: debugToken, + recaptchaEnterpriseSiteKey: recaptchaEnterpriseSiteKey + ) } } class AppCheckProviderWrapper: NSObject, AppCheckProvider { private var delegateProvider: (any AppCheckProvider)? - func configure(app: FirebaseApp, providerName: String, debugToken: String?) { + func configure( + app: FirebaseApp, + providerName: String, + debugToken: String?, + recaptchaEnterpriseSiteKey: String? + ) { switch providerName { case "debug": if let debugToken { @@ -281,6 +306,16 @@ class AppCheckProviderWrapper: NSObject, AppCheckProvider { } else { delegateProvider = DeviceCheckProvider(app: app) } + case "recaptchaEnterprise": + #if os(iOS) + if let siteKey = recaptchaEnterpriseSiteKey { + delegateProvider = RecaptchaEnterpriseProvider(app: app, siteKey: siteKey) + } else { + print("Firebase App Check: siteKey is missing for reCAPTCHA Enterprise provider.") + } + #else + print("Firebase App Check: reCAPTCHA Enterprise is only supported on iOS.") + #endif default: // deviceCheck delegateProvider = DeviceCheckProvider(app: app) 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 cd569468c12c..0c91314550cf 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 @@ -13,12 +13,14 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor AndroidAppCheckProvider, AndroidDebugProvider, AndroidPlayIntegrityProvider, + AndroidReCaptchaEnterpriseProvider, AppleProvider, AppleAppCheckProvider, AppleDebugProvider, AppleDeviceCheckProvider, AppleAppAttestProvider, AppleAppAttestWithDeviceCheckFallbackProvider, + AppleReCaptchaEnterpriseProvider, ReCaptchaEnterpriseProvider, ReCaptchaV3Provider, WebDebugProvider, diff --git a/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp b/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp index 0343b73ea815..d46886ee294e 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp +++ b/packages/firebase_app_check/firebase_app_check/windows/messages.g.cpp @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #undef _HAS_EXCEPTIONS @@ -13,16 +13,18 @@ #include #include +#include +#include #include #include #include namespace firebase_app_check_windows { -using flutter::BasicMessageChannel; -using flutter::CustomEncodableValue; -using flutter::EncodableList; -using flutter::EncodableMap; -using flutter::EncodableValue; +using ::flutter::BasicMessageChannel; +using ::flutter::CustomEncodableValue; +using ::flutter::EncodableList; +using ::flutter::EncodableMap; +using ::flutter::EncodableValue; FlutterError CreateConnectionError(const std::string channel_name) { return FlutterError( @@ -31,277 +33,419 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +namespace { +template +bool PigeonInternalDeepEquals(const T& a, const T& b); + +bool PigeonInternalDeepEquals(const double& a, const double& b); + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b); + +template +bool PigeonInternalDeepEquals(const std::optional& a, const std::optional& b); + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, const std::unique_ptr& b); + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, const ::flutter::EncodableValue& b); + +template +bool PigeonInternalDeepEquals(const T& a, const T& b) { + return a == b; +} + +template +bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b) { + if (a.size() != b.size()) { + return false; + } + for (size_t i = 0; i < a.size(); ++i) { + if (!PigeonInternalDeepEquals(a[i], b[i])) { + return false; + } + } + return true; +} + +template +bool PigeonInternalDeepEquals(const std::map& a, const std::map& b) { + if (a.size() != b.size()) { + return false; + } + for (const auto& kv : a) { + bool found = false; + for (const auto& b_kv : b) { + if (PigeonInternalDeepEquals(kv.first, b_kv.first)) { + if (PigeonInternalDeepEquals(kv.second, b_kv.second)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; +} + +bool PigeonInternalDeepEquals(const double& a, const double& b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == b) || (std::isnan(a) && std::isnan(b)); +} + +template +bool PigeonInternalDeepEquals(const std::optional& a, const std::optional& b) { + if (!a && !b) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, const std::unique_ptr& b) { + if (a.get() == b.get()) { + return true; + } + if (!a || !b) { + return false; + } + return PigeonInternalDeepEquals(*a, *b); +} + +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, const ::flutter::EncodableValue& b) { + if (a.index() != b.index()) { + return false; + } + if (const double* da = std::get_if(&a)) { + return PigeonInternalDeepEquals(*da, std::get(b)); + } else if (const ::flutter::EncodableList* la = std::get_if<::flutter::EncodableList>(&a)) { + return PigeonInternalDeepEquals(*la, std::get<::flutter::EncodableList>(b)); + } else if (const ::flutter::EncodableMap* ma = std::get_if<::flutter::EncodableMap>(&a)) { + return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); + } + return a == b; +} + +template +size_t PigeonInternalDeepHash(const T& v); + +size_t PigeonInternalDeepHash(const double& v); + +template +size_t PigeonInternalDeepHash(const std::vector& v); + +template +size_t PigeonInternalDeepHash(const std::map& v); + +template +size_t PigeonInternalDeepHash(const std::optional& v); + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v); + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v); + +template +size_t PigeonInternalDeepHash(const T& v) { + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::vector& v) { + size_t result = 1; + for (const auto& item : v) { + result = result * 31 + PigeonInternalDeepHash(item); + } + return result; +} + +template +size_t PigeonInternalDeepHash(const std::map& v) { + size_t result = 0; + for (const auto& kv : v) { + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ PigeonInternalDeepHash(kv.second)); + } + return result; +} + +size_t PigeonInternalDeepHash(const double& v) { + if (std::isnan(v)) { + // Normalize NaN to a consistent hash. + return std::hash()(std::numeric_limits::quiet_NaN()); + } + if (v == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return std::hash()(0.0); + } + return std::hash()(v); +} + +template +size_t PigeonInternalDeepHash(const std::optional& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +template +size_t PigeonInternalDeepHash(const std::unique_ptr& v) { + return v ? PigeonInternalDeepHash(*v) : 0; +} + +size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { + size_t result = v.index(); + if (const double* dv = std::get_if(&v)) { + result = result * 31 + PigeonInternalDeepHash(*dv); + } else if (const ::flutter::EncodableList* lv = + std::get_if<::flutter::EncodableList>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*lv); + } else if (const ::flutter::EncodableMap* mv = + std::get_if<::flutter::EncodableMap>(&v)) { + result = result * 31 + PigeonInternalDeepHash(*mv); + } else { + std::visit( + [&result](const auto& val) { + using T = std::decay_t; + if constexpr (!std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v && + !std::is_same_v) { + result = result * 31 + PigeonInternalDeepHash(val); + } + }, + v); + } + return result; +} + +} // namespace + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + uint8_t type, + ::flutter::ByteStreamReader* stream) const { + return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { - flutter::StandardCodecSerializer::WriteValue(value, stream); + const EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const { + ::flutter::StandardCodecSerializer::WriteValue(value, stream); } /// The codec used by FirebaseAppCheckHostApi. -const flutter::StandardMessageCodec& FirebaseAppCheckHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance( - &PigeonInternalCodecSerializer::GetInstance()); +const ::flutter::StandardMessageCodec& FirebaseAppCheckHostApi::GetCodec() { + return ::flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); } -// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through -// the `binary_messenger`. -void FirebaseAppCheckHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAppCheckHostApi* api) { +// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the `binary_messenger`. +void FirebaseAppCheckHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api) { FirebaseAppCheckHostApi::SetUp(binary_messenger, api, ""); } -void FirebaseAppCheckHostApi::SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAppCheckHostApi* api, - const std::string& message_channel_suffix) { - const std::string prepended_suffix = - message_channel_suffix.length() > 0 - ? std::string(".") + message_channel_suffix - : ""; +void FirebaseAppCheckHostApi::SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api, + const std::string& message_channel_suffix) { + const std::string prepended_suffix = message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : ""; { - BasicMessageChannel<> channel( - binary_messenger, - "dev.flutter.pigeon.firebase_app_check_platform_interface." - "FirebaseAppCheckHostApi.activate" + - prepended_suffix, - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_app_name_arg = args.at(0); - if (encodable_app_name_arg.IsNull()) { - reply(WrapError("app_name_arg unexpectedly null.")); - return; - } - const auto& app_name_arg = - std::get(encodable_app_name_arg); - const auto& encodable_android_provider_arg = args.at(1); - const auto* android_provider_arg = - std::get_if(&encodable_android_provider_arg); - const auto& encodable_apple_provider_arg = args.at(2); - const auto* apple_provider_arg = - std::get_if(&encodable_apple_provider_arg); - const auto& encodable_debug_token_arg = args.at(3); - const auto* debug_token_arg = - std::get_if(&encodable_debug_token_arg); - api->Activate(app_name_arg, android_provider_arg, - apple_provider_arg, debug_token_arg, - [reply](std::optional&& output) { - if (output.has_value()) { - reply(WrapError(output.value())); - return; - } - EncodableList wrapped; - wrapped.push_back(EncodableValue()); - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); + channel.SetMessageHandler([api](const EncodableValue& message, const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = std::get(encodable_app_name_arg); + const auto& encodable_android_provider_arg = args.at(1); + const auto* android_provider_arg = std::get_if(&encodable_android_provider_arg); + const auto& encodable_apple_provider_arg = args.at(2); + const auto* apple_provider_arg = std::get_if(&encodable_apple_provider_arg); + const auto& encodable_debug_token_arg = args.at(3); + const auto* debug_token_arg = std::get_if(&encodable_debug_token_arg); + const auto& encodable_recaptcha_enterprise_site_key_arg = args.at(4); + const auto* recaptcha_enterprise_site_key_arg = std::get_if(&encodable_recaptcha_enterprise_site_key_arg); + api->Activate(app_name_arg, android_provider_arg, apple_provider_arg, debug_token_arg, recaptcha_enterprise_site_key_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } } { - BasicMessageChannel<> channel( - binary_messenger, - "dev.flutter.pigeon.firebase_app_check_platform_interface." - "FirebaseAppCheckHostApi.getToken" + - prepended_suffix, - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_app_name_arg = args.at(0); - if (encodable_app_name_arg.IsNull()) { - reply(WrapError("app_name_arg unexpectedly null.")); - return; - } - const auto& app_name_arg = - std::get(encodable_app_name_arg); - const auto& encodable_force_refresh_arg = args.at(1); - if (encodable_force_refresh_arg.IsNull()) { - reply(WrapError("force_refresh_arg unexpectedly null.")); - return; - } - const auto& force_refresh_arg = - std::get(encodable_force_refresh_arg); - api->GetToken( - app_name_arg, force_refresh_arg, - [reply](ErrorOr>&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - auto output_optional = std::move(output).TakeValue(); - if (output_optional) { - wrapped.push_back( - EncodableValue(std::move(output_optional).value())); - } else { - wrapped.push_back(EncodableValue()); - } - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); + channel.SetMessageHandler([api](const EncodableValue& message, const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = std::get(encodable_app_name_arg); + const auto& encodable_force_refresh_arg = args.at(1); + if (encodable_force_refresh_arg.IsNull()) { + reply(WrapError("force_refresh_arg unexpectedly null.")); + return; + } + const auto& force_refresh_arg = std::get(encodable_force_refresh_arg); + api->GetToken(app_name_arg, force_refresh_arg, [reply](ErrorOr>&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + EncodableList wrapped; + auto output_optional = std::move(output).TakeValue(); + if (output_optional) { + wrapped.push_back(EncodableValue(std::move(output_optional).value())); + } else { + wrapped.push_back(EncodableValue()); } + reply(EncodableValue(std::move(wrapped))); }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } } { - BasicMessageChannel<> channel( - binary_messenger, - "dev.flutter.pigeon.firebase_app_check_platform_interface." - "FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled" + - prepended_suffix, - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_app_name_arg = args.at(0); - if (encodable_app_name_arg.IsNull()) { - reply(WrapError("app_name_arg unexpectedly null.")); - return; - } - const auto& app_name_arg = - std::get(encodable_app_name_arg); - const auto& encodable_is_token_auto_refresh_enabled_arg = - args.at(1); - if (encodable_is_token_auto_refresh_enabled_arg.IsNull()) { - reply(WrapError( - "is_token_auto_refresh_enabled_arg unexpectedly null.")); - return; - } - const auto& is_token_auto_refresh_enabled_arg = - std::get(encodable_is_token_auto_refresh_enabled_arg); - api->SetTokenAutoRefreshEnabled( - app_name_arg, is_token_auto_refresh_enabled_arg, - [reply](std::optional&& output) { - if (output.has_value()) { - reply(WrapError(output.value())); - return; - } - EncodableList wrapped; - wrapped.push_back(EncodableValue()); - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); + channel.SetMessageHandler([api](const EncodableValue& message, const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = std::get(encodable_app_name_arg); + const auto& encodable_is_token_auto_refresh_enabled_arg = args.at(1); + if (encodable_is_token_auto_refresh_enabled_arg.IsNull()) { + reply(WrapError("is_token_auto_refresh_enabled_arg unexpectedly null.")); + return; + } + const auto& is_token_auto_refresh_enabled_arg = std::get(encodable_is_token_auto_refresh_enabled_arg); + api->SetTokenAutoRefreshEnabled(app_name_arg, is_token_auto_refresh_enabled_arg, [reply](std::optional&& output) { + if (output.has_value()) { + reply(WrapError(output.value())); + return; } + EncodableList wrapped; + wrapped.push_back(EncodableValue()); + reply(EncodableValue(std::move(wrapped))); }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } } { - BasicMessageChannel<> channel( - binary_messenger, - "dev.flutter.pigeon.firebase_app_check_platform_interface." - "FirebaseAppCheckHostApi.registerTokenListener" + - prepended_suffix, - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_app_name_arg = args.at(0); - if (encodable_app_name_arg.IsNull()) { - reply(WrapError("app_name_arg unexpectedly null.")); - return; - } - const auto& app_name_arg = - std::get(encodable_app_name_arg); - api->RegisterTokenListener( - app_name_arg, [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back( - EncodableValue(std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); + channel.SetMessageHandler([api](const EncodableValue& message, const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = std::get(encodable_app_name_arg); + api->RegisterTokenListener(app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } } { - BasicMessageChannel<> channel( - binary_messenger, - "dev.flutter.pigeon.firebase_app_check_platform_interface." - "FirebaseAppCheckHostApi.getLimitedUseAppCheckToken" + - prepended_suffix, - &GetCodec()); + BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken" + prepended_suffix, &GetCodec()); if (api != nullptr) { - channel.SetMessageHandler( - [api](const EncodableValue& message, - const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_app_name_arg = args.at(0); - if (encodable_app_name_arg.IsNull()) { - reply(WrapError("app_name_arg unexpectedly null.")); - return; - } - const auto& app_name_arg = - std::get(encodable_app_name_arg); - api->GetLimitedUseAppCheckToken( - app_name_arg, [reply](ErrorOr&& output) { - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - EncodableList wrapped; - wrapped.push_back( - EncodableValue(std::move(output).TakeValue())); - reply(EncodableValue(std::move(wrapped))); - }); - } catch (const std::exception& exception) { - reply(WrapError(exception.what())); + channel.SetMessageHandler([api](const EncodableValue& message, const ::flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_app_name_arg = args.at(0); + if (encodable_app_name_arg.IsNull()) { + reply(WrapError("app_name_arg unexpectedly null.")); + return; + } + const auto& app_name_arg = std::get(encodable_app_name_arg); + api->GetLimitedUseAppCheckToken(app_name_arg, [reply](ErrorOr&& output) { + if (output.has_error()) { + reply(WrapError(output.error())); + return; } + EncodableList wrapped; + wrapped.push_back(EncodableValue(std::move(output).TakeValue())); + reply(EncodableValue(std::move(wrapped))); }); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); } else { channel.SetMessageHandler(nullptr); } } } -EncodableValue FirebaseAppCheckHostApi::WrapError( - std::string_view error_message) { - return EncodableValue( - EncodableList{EncodableValue(std::string(error_message)), - EncodableValue("Error"), EncodableValue()}); +EncodableValue FirebaseAppCheckHostApi::WrapError(std::string_view error_message) { + return EncodableValue(EncodableList{ + EncodableValue(std::string(error_message)), + EncodableValue("Error"), + EncodableValue() + }); } EncodableValue FirebaseAppCheckHostApi::WrapError(const FlutterError& error) { - return EncodableValue(EncodableList{EncodableValue(error.code()), - EncodableValue(error.message()), - error.details()}); + return EncodableValue(EncodableList{ + EncodableValue(error.code()), + EncodableValue(error.message()), + error.details() + }); } } // namespace firebase_app_check_windows diff --git a/packages/firebase_app_check/firebase_app_check/windows/messages.g.h b/packages/firebase_app_check/firebase_app_check/windows/messages.g.h index c56d71862604..4d13d5ff3fe4 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/messages.g.h +++ b/packages/firebase_app_check/firebase_app_check/windows/messages.g.h @@ -1,7 +1,7 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon #ifndef PIGEON_MESSAGES_G_H_ @@ -17,29 +17,29 @@ namespace firebase_app_check_windows { + // Generated class from Pigeon. class FlutterError { public: - explicit FlutterError(const std::string& code) : code_(code) {} + explicit FlutterError(const std::string& code) + : code_(code) {} explicit FlutterError(const std::string& code, const std::string& message) - : code_(code), message_(message) {} - explicit FlutterError(const std::string& code, const std::string& message, - const flutter::EncodableValue& details) - : code_(code), message_(message), details_(details) {} + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, const ::flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } - const flutter::EncodableValue& details() const { return details_; } + const ::flutter::EncodableValue& details() const { return details_; } private: std::string code_; std::string message_; - flutter::EncodableValue details_; + ::flutter::EncodableValue details_; }; -template -class ErrorOr { +template class ErrorOr { public: ErrorOr(const T& rhs) : v_(rhs) {} ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} @@ -58,7 +58,9 @@ class ErrorOr { std::variant v_; }; -class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { + + +class PigeonInternalCodecSerializer : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -66,51 +68,55 @@ class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { return sInstance; } - void WriteValue(const flutter::EncodableValue& value, - flutter::ByteStreamWriter* stream) const override; - + void WriteValue( + const ::flutter::EncodableValue& value, + ::flutter::ByteStreamWriter* stream) const override; protected: - flutter::EncodableValue ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const override; + ::flutter::EncodableValue ReadValueOfType( + uint8_t type, + ::flutter::ByteStreamReader* stream) const override; }; -// Generated interface from Pigeon that represents a handler of messages from -// Flutter. +// Generated interface from Pigeon that represents a handler of messages from Flutter. class FirebaseAppCheckHostApi { public: FirebaseAppCheckHostApi(const FirebaseAppCheckHostApi&) = delete; FirebaseAppCheckHostApi& operator=(const FirebaseAppCheckHostApi&) = delete; virtual ~FirebaseAppCheckHostApi() {} virtual void Activate( - const std::string& app_name, const std::string* android_provider, - const std::string* apple_provider, const std::string* debug_token, - std::function reply)> result) = 0; + const std::string& app_name, + const std::string* android_provider, + const std::string* apple_provider, + const std::string* debug_token, + const std::string* recaptcha_enterprise_site_key, + std::function reply)> result) = 0; virtual void GetToken( - const std::string& app_name, bool force_refresh, - std::function> reply)> - result) = 0; + const std::string& app_name, + bool force_refresh, + std::function> reply)> result) = 0; virtual void SetTokenAutoRefreshEnabled( - const std::string& app_name, bool is_token_auto_refresh_enabled, - std::function reply)> result) = 0; + const std::string& app_name, + bool is_token_auto_refresh_enabled, + std::function reply)> result) = 0; virtual void RegisterTokenListener( - const std::string& app_name, - std::function reply)> result) = 0; + const std::string& app_name, + std::function reply)> result) = 0; virtual void GetLimitedUseAppCheckToken( - const std::string& app_name, - std::function reply)> result) = 0; + const std::string& app_name, + std::function reply)> result) = 0; // The codec used by FirebaseAppCheckHostApi. - static const flutter::StandardMessageCodec& GetCodec(); - // Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through - // the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAppCheckHostApi* api); - static void SetUp(flutter::BinaryMessenger* binary_messenger, - FirebaseAppCheckHostApi* api, - const std::string& message_channel_suffix); - static flutter::EncodableValue WrapError(std::string_view error_message); - static flutter::EncodableValue WrapError(const FlutterError& error); - + static const ::flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the `binary_messenger`. + static void SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api); + static void SetUp( + ::flutter::BinaryMessenger* binary_messenger, + FirebaseAppCheckHostApi* api, + const std::string& message_channel_suffix); + static ::flutter::EncodableValue WrapError(std::string_view error_message); + static ::flutter::EncodableValue WrapError(const FlutterError& error); protected: FirebaseAppCheckHostApi() = default; }; 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 index c827b3184022..98337af759c6 100644 --- 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 @@ -32,3 +32,15 @@ class AndroidDebugProvider extends AndroidAppCheckProvider { class AndroidPlayIntegrityProvider extends AndroidAppCheckProvider { const AndroidPlayIntegrityProvider() : super('playIntegrity'); } + +/// reCAPTCHA Enterprise provider for Android. +/// +/// See documentation: https://firebase.google.com/docs/app-check/android/recaptcha-enterprise-provider +class AndroidReCaptchaEnterpriseProvider extends AndroidAppCheckProvider { + /// Creates an Android reCAPTCHA Enterprise provider with a site key. + const AndroidReCaptchaEnterpriseProvider({required this.siteKey}) + : super('recaptchaEnterprise'); + + /// The reCAPTCHA Enterprise site key. + final String siteKey; +} 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 index 5e6af1ce2359..f2ca2f6e548f 100644 --- 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 @@ -54,3 +54,15 @@ class AppleAppAttestWithDeviceCheckFallbackProvider const AppleAppAttestWithDeviceCheckFallbackProvider() : super('appAttestWithDeviceCheckFallback'); } + +/// reCAPTCHA Enterprise provider for Apple platforms. +/// +/// See documentation: https://firebase.google.com/docs/app-check/ios/recaptcha-enterprise-provider +class AppleReCaptchaEnterpriseProvider extends AppleAppCheckProvider { + /// Creates an Apple reCAPTCHA Enterprise provider with a site key. + const AppleReCaptchaEnterpriseProvider({required this.siteKey}) + : super('recaptchaEnterprise'); + + /// The reCAPTCHA Enterprise site key. + final String siteKey; +} 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 b7f2d035d212..5d157aea948e 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 @@ -112,6 +112,13 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { WindowsAppCheckProvider? providerWindows, }) async { try { + String? recaptchaEnterpriseSiteKey; + if (providerAndroid is AndroidReCaptchaEnterpriseProvider) { + recaptchaEnterpriseSiteKey = providerAndroid.siteKey; + } else if (providerApple is AppleReCaptchaEnterpriseProvider) { + recaptchaEnterpriseSiteKey = providerApple.siteKey; + } + await _pigeonApi.activate( app.name, defaultTargetPlatform == TargetPlatform.android || kDebugMode @@ -133,6 +140,7 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { providerApple: providerApple, providerWindows: providerWindows, ), + recaptchaEnterpriseSiteKey, ); } on PlatformException catch (e, s) { convertPlatformException(e, s); diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart index 8916928599ce..8fb14b67fa11 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/pigeon/messages.pigeon.dart @@ -1,23 +1,44 @@ // Copyright 2025, the Chromium project authors. Please see the AUTHORS file // 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. -// Autogenerated from Pigeon (v25.3.2), do not edit directly. +// Autogenerated from Pigeon (v26.3.4), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } + + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -43,156 +64,105 @@ class FirebaseAppCheckHostApi { /// Constructor for [FirebaseAppCheckHostApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - FirebaseAppCheckHostApi( - {BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + FirebaseAppCheckHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = - messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; final BinaryMessenger? pigeonVar_binaryMessenger; static const MessageCodec pigeonChannelCodec = _PigeonCodec(); final String pigeonVar_messageChannelSuffix; - Future activate(String appName, String? androidProvider, - String? appleProvider, String? debugToken) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future activate(String appName, String? androidProvider, String? appleProvider, String? debugToken, String? recaptchaEnterpriseSiteKey) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel - .send([appName, androidProvider, appleProvider, debugToken]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, androidProvider, appleProvider, debugToken, recaptchaEnterpriseSiteKey]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future getToken(String appName, bool forceRefresh) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName, forceRefresh]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return (pigeonVar_replyList[0] as String?); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, forceRefresh]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; + return pigeonVar_replyValue as String?; } - Future setTokenAutoRefreshEnabled( - String appName, bool isTokenAutoRefreshEnabled) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + Future setTokenAutoRefreshEnabled(String appName, bool isTokenAutoRefreshEnabled) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future registerTokenListener(String appName) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as String?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as String; } Future getLimitedUseAppCheckToken(String appName) async { - final String pigeonVar_channelName = - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = - BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); - final List? pigeonVar_replyList = - await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as String?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as String; } } diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart index e84ff78ab5f4..e20e1bdfba0e 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/pigeons/messages.dart @@ -29,8 +29,10 @@ abstract class FirebaseAppCheckHostApi { String? androidProvider, String? appleProvider, String? debugToken, + String? recaptchaEnterpriseSiteKey, ); + @async String? getToken(String appName, bool forceRefresh); 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 0839886ba264..395e96ef5134 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 @@ -7,6 +7,7 @@ import 'package:firebase_app_check_platform_interface/src/pigeon/messages.pigeon import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/services.dart'; import '../mock.dart'; @@ -121,5 +122,58 @@ void main() { expect(calls.single[3], 'android-debug-token'); }); }); + + group('activate()', () { + test('passes recaptchaEnterpriseSiteKey on Android', () async { + final appCheck = MethodChannelFirebaseAppCheck(app: Firebase.app()); + + final List log = []; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (message) async { + final list = const StandardMessageCodec().decodeMessage(message) as List; + log.add(list); + return const StandardMessageCodec().encodeMessage([null]); // Return success + }, + ); + + await appCheck.activate( + providerAndroid: const AndroidReCaptchaEnterpriseProvider(siteKey: 'my-site-key'), + ); + + expect(log.length, 1); + expect(log[0][0], '[DEFAULT]'); // appName + expect(log[0][1], 'recaptchaEnterprise'); // androidProvider + expect(log[0][4], 'my-site-key'); // recaptchaEnterpriseSiteKey + }); + + test('passes recaptchaEnterpriseSiteKey on iOS', () async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + final appCheck = MethodChannelFirebaseAppCheck(app: Firebase.app()); + + final List log = []; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + (message) async { + final list = const StandardMessageCodec().decodeMessage(message) as List; + log.add(list); + return const StandardMessageCodec().encodeMessage([null]); // Return success + }, + ); + + await appCheck.activate( + providerApple: const AppleReCaptchaEnterpriseProvider(siteKey: 'my-apple-site-key'), + ); + + expect(log.length, 1); + expect(log[0][0], '[DEFAULT]'); // appName + expect(log[0][2], 'recaptchaEnterprise'); // appleProvider + expect(log[0][4], 'my-apple-site-key'); // recaptchaEnterpriseSiteKey + }); + }); }); }