From 976a491ae7589e7862239a6e11a33437dd00a64b Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Sun, 12 Apr 2026 16:02:42 -0400 Subject: [PATCH 1/6] feat(app_check,windows): add custom provider support for Windows --- .../appcheck/FirebaseAppCheckPlugin.kt | 1 + .../GeneratedAndroidFirebaseAppCheck.g.kt | 37 +- .../FirebaseAppCheckMessages.g.swift | 156 +++---- .../FirebaseAppCheckPlugin.swift | 2 +- .../lib/firebase_app_check.dart | 4 +- .../firebase_app_check/windows/CMakeLists.txt | 75 ++++ .../windows/firebase_app_check_plugin.cpp | 81 +++- .../windows/firebase_app_check_plugin.h | 38 ++ .../firebase_app_check/windows/messages.g.cpp | 403 +++++++++--------- .../firebase_app_check/windows/messages.g.h | 89 ++-- ...firebase_app_check_platform_interface.dart | 1 + .../method_channel_firebase_app_check.dart | 5 + .../lib/src/pigeon/messages.pigeon.dart | 99 +++-- .../lib/src/windows_providers.dart | 18 + .../pigeons/messages.dart | 12 + .../firebase_core/windows/CMakeLists.txt | 9 +- 16 files changed, 659 insertions(+), 371 deletions(-) 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..7ccb625d0a97 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 @@ -55,6 +55,7 @@ class FirebaseAppCheckPlugin : androidProvider: String?, appleProvider: String?, debugToken: String?, + windowsProvider: String?, callback: (Result) -> Unit ) { try { 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..7136c50b5d45 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 @@ -18,6 +18,9 @@ import java.io.ByteArrayOutputStream import java.nio.ByteBuffer private object GeneratedAndroidFirebaseAppCheckPigeonUtils { + fun createConnectionError(channelName: String): FlutterError { + return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") } + fun wrapResult(result: Any?): List { return listOf(result) } @@ -62,7 +65,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?, windowsProvider: 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 +89,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 windowsProviderArg = args[4] as String? + api.activate(appNameArg, androidProviderArg, appleProviderArg, debugTokenArg, windowsProviderArg) { result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) @@ -183,3 +187,32 @@ interface FirebaseAppCheckHostApi { } } } +/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +class FirebaseAppCheckFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { + companion object { + /** The codec used by FirebaseAppCheckFlutterApi. */ + val codec: MessageCodec by lazy { + GeneratedAndroidFirebaseAppCheckPigeonCodec() + } + } + fun getCustomToken(callback: (Result) -> Unit) +{ + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$separatedMessageChannelSuffix" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(null) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else if (it[0] == null) { + callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", ""))) + } else { + val output = it[0] as String + callback(Result.success(output)) + } + } else { + callback(Result.failure(GeneratedAndroidFirebaseAppCheckPigeonUtils.createConnectionError(channelName))) + } + } + } +} 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..6534ba56a79e 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 @@ -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?] { @@ -57,8 +58,12 @@ private func wrapError(_ error: Any) -> [Any?] { ] } +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + 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 +71,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?, windowsProvider: 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 windowsProviderArg: String? = nilOrValue(args[4]) + api.activate(appName: appNameArg, androidProvider: androidProviderArg, appleProvider: appleProviderArg, debugToken: debugTokenArg, windowsProvider: windowsProviderArg) { result in switch result { case .success: reply(wrapResult(nil)) - case let .failure(error): + case .failure(let error): reply(wrapError(error)) } } @@ -140,21 +129,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 +147,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 +165,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 +182,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)) } } @@ -231,3 +201,39 @@ class FirebaseAppCheckHostApiSetup { } } } +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol FirebaseAppCheckFlutterApiProtocol { + func getCustomToken(completion: @escaping (Result) -> Void) +} +class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: FirebaseAppCheckMessagesPigeonCodec { + return FirebaseAppCheckMessagesPigeonCodec.shared + } + func getCustomToken(completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage(nil) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else if listResponse[0] == nil { + completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) + } else { + let result = listResponse[0] as! String + completion(.success(result)) + } + } + } +} 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..a7efddd615ec 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?, windowsProvider: String?, completion: @escaping (Result) -> Void) { guard let app = FLTFirebasePlugin.firebaseAppNamed(appName) else { completion(.failure(FlutterError( 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..68cef9f227d0 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 @@ -23,7 +23,9 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor ReCaptchaV3Provider, WebDebugProvider, WindowsAppCheckProvider, - WindowsDebugProvider; + WindowsDebugProvider, + WindowsCustomProvider, + FirebaseAppCheckFlutterApi; export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' show FirebaseException; diff --git a/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt b/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt index 7c40c200c5e9..10fd9a991423 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt +++ b/packages/firebase_app_check/firebase_app_check/windows/CMakeLists.txt @@ -4,6 +4,53 @@ # customers of the plugin. cmake_minimum_required(VERSION 3.14) +set(FIREBASE_SDK_VERSION "13.5.0") + +if(EXISTS $ENV{FIREBASE_CPP_SDK_DIR}/include/firebase/version.h) + file(READ "$ENV{FIREBASE_CPP_SDK_DIR}/include/firebase/version.h" existing_version) + + string(REGEX MATCH "FIREBASE_VERSION_MAJOR ([0-9]*)" _ ${existing_version}) + set(existing_version_major ${CMAKE_MATCH_1}) + + string(REGEX MATCH "FIREBASE_VERSION_MINOR ([0-9]*)" _ ${existing_version}) + set(existing_version_minor ${CMAKE_MATCH_1}) + + string(REGEX MATCH "FIREBASE_VERSION_REVISION ([0-9]*)" _ ${existing_version}) + set(existing_version_revision ${CMAKE_MATCH_1}) + + set(existing_version "${existing_version_major}.${existing_version_minor}.${existing_version_revision}") +endif() + +if(existing_version VERSION_EQUAL FIREBASE_SDK_VERSION) + message(STATUS "Found Firebase SDK version ${existing_version}") + set(FIREBASE_CPP_SDK_DIR $ENV{FIREBASE_CPP_SDK_DIR}) +else() + set(firebase_sdk_url "https://dl.google.com/firebase/sdk/cpp/firebase_cpp_sdk_windows_${FIREBASE_SDK_VERSION}.zip") + set(firebase_sdk_filename "${CMAKE_BINARY_DIR}/firebase_cpp_sdk_windows_${FIREBASE_SDK_VERSION}.zip") + set(extracted_path "${CMAKE_BINARY_DIR}/extracted") + if(NOT EXISTS ${firebase_sdk_filename}) + file(DOWNLOAD ${firebase_sdk_url} ${firebase_sdk_filename} + SHOW_PROGRESS + STATUS download_status + LOG download_log) + list(GET download_status 0 status_code) + if(NOT status_code EQUAL 0) + message(FATAL_ERROR "Download failed: ${download_log}") + endif() + else() + message(STATUS "Using cached Firebase SDK zip file") + endif() + + if(NOT EXISTS ${extracted_path}) + file(MAKE_DIRECTORY ${extracted_path}) + file(ARCHIVE_EXTRACT INPUT ${firebase_sdk_filename} + DESTINATION ${extracted_path}) + else() + message(STATUS "Using cached extracted Firebase SDK") + endif() + set(FIREBASE_CPP_SDK_DIR "${extracted_path}/firebase_cpp_sdk_windows") +endif() + # Project-level configuration. set(PROJECT_NAME "firebase_app_check") project(${PROJECT_NAME} LANGUAGES CXX) @@ -66,6 +113,34 @@ target_compile_definitions(${PLUGIN_NAME} PRIVATE -DINTERNAL_EXPERIMENTAL=1) # dependencies here. set(MSVC_RUNTIME_MODE MD) set(firebase_libs firebase_core_plugin firebase_app_check) + +set(FLUTTERFIRE_FIREBASE_CPP_SDK_BINARY_DIR + "${CMAKE_BINARY_DIR}/firebase_cpp_sdk") +if(NOT TARGET firebase_app) + add_subdirectory(${FIREBASE_CPP_SDK_DIR} + ${FLUTTERFIRE_FIREBASE_CPP_SDK_BINARY_DIR} + EXCLUDE_FROM_ALL) +endif() + +target_include_directories(${PLUGIN_NAME} INTERFACE + "${FIREBASE_CPP_SDK_DIR}/include") + +# firebase_core rewrites the imported Firebase C++ SDK library targets so +# multi-config generators (Visual Studio) use the Release .lib paths when +# building Release/Profile. Without this, firebase_app_check can end up linking +# the Debug firebase_app_check.lib into a Release runner, which pulls in debug +# CRT symbols and fails at link time. +get_target_property(firebase_app_check_debug_path firebase_app_check IMPORTED_LOCATION) +if(firebase_app_check_debug_path) + string(REPLACE "Debug" "Release" firebase_app_check_release_path + ${firebase_app_check_debug_path}) + set_target_properties(firebase_app_check PROPERTIES + IMPORTED_LOCATION_DEBUG "${firebase_app_check_debug_path}" + IMPORTED_LOCATION_RELEASE "${firebase_app_check_release_path}" + IMPORTED_LOCATION_PROFILE "${firebase_app_check_release_path}" + ) +endif() + target_link_libraries(${PLUGIN_NAME} PRIVATE "${firebase_libs}") target_include_directories(${PLUGIN_NAME} INTERFACE diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp index d9a0a72d7014..0b7a7ec4332c 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -98,6 +99,57 @@ class TokenStreamHandler std::unique_ptr listener_; }; +// FlutterCustomAppCheckProvider — calls into Dart via the FlutterApi and +// completes the Firebase C++ SDK callback asynchronously when Dart returns a +// token (or an error). +FlutterCustomAppCheckProvider::FlutterCustomAppCheckProvider( + flutter::BinaryMessenger* binary_messenger) + : flutter_api_( + std::make_unique(binary_messenger)) {} + +void FlutterCustomAppCheckProvider::GetToken( + std::function + completion_callback) { + auto completion = std::make_shared< + std::function>(std::move(completion_callback)); + + flutter_api_->GetCustomToken( + [completion](const std::string& token) { + firebase::app_check::AppCheckToken result_token; + result_token.token = token; + // Set expiry to 55 minutes from now (server mints 1-hour tokens; + // 5-minute buffer avoids using a nearly-expired token). + result_token.expire_time_millis = + static_cast( + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count()) + + 55LL * 60LL * 1000LL; + (*completion)(result_token, firebase::app_check::kAppCheckErrorNone, + ""); + }, + [completion](const FlutterError& error) { + (*completion)(firebase::app_check::AppCheckToken(), + firebase::app_check::kAppCheckErrorUnknown, + error.message().empty() ? "unknown" : error.message()); + }); +} + +FlutterCustomAppCheckProviderFactory::FlutterCustomAppCheckProviderFactory( + flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger) {} + +firebase::app_check::AppCheckProvider* +FlutterCustomAppCheckProviderFactory::CreateProvider(firebase::App* app) { + if (!provider_) { + provider_ = + std::make_unique(binary_messenger_); + } + return provider_.get(); +} + static AppCheck* GetAppCheckFromPigeon(const std::string& app_name) { App* app = App::GetInstance(app_name.c_str()); return AppCheck::GetInstance(app); @@ -166,17 +218,23 @@ FirebaseAppCheckPlugin::~FirebaseAppCheckPlugin() { void FirebaseAppCheckPlugin::Activate( const std::string& app_name, const std::string* android_provider, const std::string* apple_provider, const std::string* debug_token, + const std::string* windows_provider, std::function reply)> result) { - // On Windows/desktop, only the Debug provider is available. - DebugAppCheckProviderFactory* factory = - DebugAppCheckProviderFactory::GetInstance(); + if (windows_provider != nullptr && *windows_provider == "custom") { + custom_provider_factory_ = + std::make_unique( + binaryMessenger); + AppCheck::SetAppCheckProviderFactory(custom_provider_factory_.get()); + } else { + DebugAppCheckProviderFactory* factory = + DebugAppCheckProviderFactory::GetInstance(); + + if (debug_token != nullptr && !debug_token->empty()) { + factory->SetDebugToken(*debug_token); + } - if (debug_token != nullptr && !debug_token->empty()) { - factory->SetDebugToken(*debug_token); + AppCheck::SetAppCheckProviderFactory(factory); } - - AppCheck::SetAppCheckProviderFactory(factory); - result(std::nullopt); } @@ -229,9 +287,12 @@ void FirebaseAppCheckPlugin::RegisterTokenListener( void FirebaseAppCheckPlugin::GetLimitedUseAppCheckToken( const std::string& app_name, std::function reply)> result) { + // GetLimitedUseAppCheckToken was added to the Firebase C++ SDK after the + // version currently bundled with this plugin. Fall back to GetAppCheckToken, + // which is functionally equivalent for our custom Windows provider since it + // does not cache — it calls getWindowsAppCheckToken on every invocation. AppCheck* app_check = GetAppCheckFromPigeon(app_name); - - Future future = app_check->GetLimitedUseAppCheckToken(); + Future future = app_check->GetAppCheckToken(false); future.OnCompletion([result](const Future& completed_future) { if (completed_future.error() != 0) { result(ParseError(completed_future)); diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h index baabf2bd5931..c78375ffbcb4 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -24,6 +25,37 @@ namespace firebase_app_check_windows { class TokenStreamHandler; +// Custom App Check provider for Windows. When the Firebase C++ SDK calls +// GetToken(), this provider calls into Dart via FirebaseAppCheckFlutterApi +// to request a server-minted token (from the getWindowsAppCheckToken Cloud +// Function), then completes the SDK callback with the result. +class FlutterCustomAppCheckProvider + : public firebase::app_check::AppCheckProvider { + public: + explicit FlutterCustomAppCheckProvider( + flutter::BinaryMessenger* binary_messenger); + void GetToken(std::function + completion_callback) override; + + private: + std::unique_ptr flutter_api_; +}; + +// Factory that creates FlutterCustomAppCheckProvider instances. +class FlutterCustomAppCheckProviderFactory + : public firebase::app_check::AppCheckProviderFactory { + public: + explicit FlutterCustomAppCheckProviderFactory( + flutter::BinaryMessenger* binary_messenger); + firebase::app_check::AppCheckProvider* CreateProvider( + firebase::App* app) override; + + private: + flutter::BinaryMessenger* binary_messenger_; + std::unique_ptr provider_; +}; + class FirebaseAppCheckPlugin : public flutter::Plugin, public FirebaseAppCheckHostApi { friend class TokenStreamHandler; @@ -43,6 +75,7 @@ class FirebaseAppCheckPlugin : public flutter::Plugin, void Activate( const std::string& app_name, const std::string* android_provider, const std::string* apple_provider, const std::string* debug_token, + const std::string* windows_provider, std::function reply)> result) override; void GetToken(const std::string& app_name, bool force_refresh, std::function> reply)> @@ -58,6 +91,11 @@ class FirebaseAppCheckPlugin : public flutter::Plugin, std::function reply)> result) override; private: + // Holds ownership of the custom provider factory for its lifetime. + // Must outlive the AppCheck instance it was registered with. + std::unique_ptr + custom_provider_factory_; + static flutter::BinaryMessenger* binaryMessenger; static std::map< std::string, 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..bb380c01c196 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 @@ -31,277 +31,262 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, flutter::ByteStreamReader* stream) const { + uint8_t type, + flutter::ByteStreamReader* stream) const { return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + 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()); + 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_windows_provider_arg = args.at(4); + const auto* windows_provider_arg = std::get_if(&encodable_windows_provider_arg); + api->Activate(app_name_arg, android_provider_arg, apple_provider_arg, debug_token_arg, windows_provider_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() + }); +} + +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi(flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), + message_channel_suffix_("") {} + +FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + +const flutter::StandardMessageCodec& FirebaseAppCheckFlutterApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); +} + +void FirebaseAppCheckFlutterApi::GetCustomToken( + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken" + message_channel_suffix_; + BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); + EncodableValue encoded_api_arguments = EncodableValue(); + channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { + std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + const auto& encodable_return_value = *response; + const auto* list_return_value = std::get_if(&encodable_return_value); + if (list_return_value) { + if (list_return_value->size() > 1) { + on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + } else { + const auto& return_value = std::get(list_return_value->at(0)); + on_success(return_value); + } + } else { + on_error(CreateConnectionError(channel_name)); + } + }); } } // 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..292f60d0a0d8 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 @@ -17,16 +17,17 @@ 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_; } @@ -38,8 +39,7 @@ class FlutterError { flutter::EncodableValue details_; }; -template -class ErrorOr { +template class ErrorOr { public: ErrorOr(const T& rhs) : v_(rhs) {} ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} @@ -52,12 +52,15 @@ class ErrorOr { private: friend class FirebaseAppCheckHostApi; + friend class FirebaseAppCheckFlutterApi; ErrorOr() = default; T TakeValue() && { return std::get(std::move(v_)); } std::variant v_; }; + + class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); @@ -66,53 +69,73 @@ 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; + 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* windows_provider, + 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); + // 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; }; +// Generated class from Pigeon that represents Flutter messages that can be called from C++. +class FirebaseAppCheckFlutterApi { + public: + FirebaseAppCheckFlutterApi(flutter::BinaryMessenger* binary_messenger); + FirebaseAppCheckFlutterApi( + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); + static const flutter::StandardMessageCodec& GetCodec(); + void GetCustomToken( + std::function&& on_success, + std::function&& on_error); + private: + flutter::BinaryMessenger* binary_messenger_; + std::string message_channel_suffix_; +}; + } // namespace firebase_app_check_windows #endif // PIGEON_MESSAGES_G_H_ 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 53a285e48729..27a14c5a368b 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 @@ -11,3 +11,4 @@ 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'; export 'src/windows_providers.dart'; +export 'src/pigeon/messages.pigeon.dart' show FirebaseAppCheckFlutterApi; 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 96afb6f2c999..ae9e0f8ab2cc 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 @@ -93,6 +93,7 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { }) async { try { String? debugToken; + String? windowsProvider; if (providerAndroid is AndroidDebugProvider && providerAndroid.debugToken != null) { debugToken = providerAndroid.debugToken; @@ -103,6 +104,9 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { providerWindows.debugToken != null) { debugToken = providerWindows.debugToken; } + if (!kIsWeb && defaultTargetPlatform == TargetPlatform.windows) { + windowsProvider = providerWindows?.type; + } await _pigeonApi.activate( app.name, @@ -121,6 +125,7 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { ) : null, debugToken, + windowsProvider, ); } 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 fab0d53008a2..89868148521f 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 @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. // Autogenerated from Pigeon (v25.3.2), 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, require_trailing_commas +// 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 import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -18,6 +18,17 @@ PlatformException _createConnectionError(String channelName) { ); } +List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { + if (empty) { + return []; + } + if (error == null) { + return [result]; + } + return [error.code, error.message, error.details]; +} + + class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -43,29 +54,23 @@ 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? windowsProvider) async { + final String pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel - .send([appName, androidProvider, appleProvider, debugToken]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, androidProvider, appleProvider, debugToken, windowsProvider]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -82,16 +87,13 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName, forceRefresh]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, forceRefresh]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -107,18 +109,14 @@ class FirebaseAppCheckHostApi { } } - 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 String pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -135,16 +133,13 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -166,16 +161,13 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = - pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -196,3 +188,32 @@ class FirebaseAppCheckHostApi { } } } + +abstract class FirebaseAppCheckFlutterApi { + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + Future getCustomToken(); + + static void setUp(FirebaseAppCheckFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + { + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$messageChannelSuffix', pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (api == null) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + try { + final String output = await api.getCustomToken(); + return wrapResponse(result: output); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } +} diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart index b6b09e55b20b..b5c4e78b3ac4 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart @@ -12,6 +12,24 @@ abstract class WindowsAppCheckProvider { const WindowsAppCheckProvider(this.type); } +/// Custom provider for Windows production builds. +/// +/// When activated, the Windows C++ plugin registers a custom +/// [AppCheckProvider] that calls into Dart via a Pigeon FlutterApi each time +/// the Firebase SDK needs a fresh token. The Dart handler is expected to call +/// a server-side Cloud Function (e.g. `getWindowsAppCheckToken`) that mints a +/// valid App Check token using the Firebase Admin SDK and returns it. +/// +/// Register the Dart token handler before any Firestore operations: +/// ```dart +/// FirebaseAppCheckFlutterApi.setUp(myHandler); +/// ``` +/// +/// **Do not use [WindowsDebugProvider] in production builds.** +class WindowsCustomProvider extends WindowsAppCheckProvider { + const WindowsCustomProvider() : super('custom'); +} + /// Debug provider for Windows. /// /// This is the **only** provider available on Windows. Unlike mobile platforms, 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..ffdd7dcfa199 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,6 +29,7 @@ abstract class FirebaseAppCheckHostApi { String? androidProvider, String? appleProvider, String? debugToken, + String? windowsProvider, ); @async @@ -46,3 +47,14 @@ abstract class FirebaseAppCheckHostApi { @async String getLimitedUseAppCheckToken(String appName); } + +// Dart-side handler invoked by C++ when the Firebase SDK needs a fresh App +// Check token on Windows. Implementations call getWindowsAppCheckToken (a +// Cloud Function with enforceAppCheck:false) and return the minted token +// string. The C++ side blocks until the Future resolves, then hands the token +// to the Firebase SDK, which attaches it to every subsequent Firestore request. +@FlutterApi() +abstract class FirebaseAppCheckFlutterApi { + @async + String getCustomToken(); +} diff --git a/packages/firebase_core/firebase_core/windows/CMakeLists.txt b/packages/firebase_core/firebase_core/windows/CMakeLists.txt index 277ea0e10c24..86c39c045600 100644 --- a/packages/firebase_core/firebase_core/windows/CMakeLists.txt +++ b/packages/firebase_core/firebase_core/windows/CMakeLists.txt @@ -118,7 +118,13 @@ if(NOT MSVC_RUNTIME_MODE) set(MSVC_RUNTIME_MODE MD) endif() -add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +set(FLUTTERFIRE_FIREBASE_CPP_SDK_BINARY_DIR + "${CMAKE_BINARY_DIR}/firebase_cpp_sdk") +if(NOT TARGET firebase_app) + add_subdirectory(${FIREBASE_CPP_SDK_DIR} + ${FLUTTERFIRE_FIREBASE_CPP_SDK_BINARY_DIR} + EXCLUDE_FROM_ALL) +endif() target_include_directories(${PLUGIN_NAME} INTERFACE "${FIREBASE_CPP_SDK_DIR}/include") @@ -129,6 +135,7 @@ foreach(firebase_lib IN ITEMS ${FIREBASE_RELEASE_PATH_LIBS}) set_target_properties(${firebase_lib} PROPERTIES IMPORTED_LOCATION_DEBUG "${firebase_lib_path}" IMPORTED_LOCATION_RELEASE "${firebase_lib_release_path}" + IMPORTED_LOCATION_PROFILE "${firebase_lib_release_path}" ) endforeach() From b7f0ba25fe236d2f1a9c7f129c5b82927cd57345 Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Sun, 12 Apr 2026 23:00:33 -0400 Subject: [PATCH 2/6] refactor(app_check,windows): return token and expiry from Dart custom provider, clarify provider docs The Dart FlutterApi now returns CustomAppCheckToken, a class carrying both the minted token and its wall-clock expiry in Unix epoch milliseconds, instead of just a string. This lets backends mint tokens with arbitrary lifetimes without the plugin hardcoding a refresh window, and it removes a 55-minute constant from the C++ plugin that assumed every backend mints ~1-hour tokens. Short-lived tokens for stricter posture and longer-lived tokens for fewer round-trips are both supported by the same API. The Windows plugin's GetToken now pipes the expiry from Dart straight through to AppCheckToken.expire_time_millis, so the Firebase C++ SDK caches for the exact lifetime the backend chose. Also updates stale provider docs on windows_providers.dart (the base and debug-provider docs previously described debug as the only supported Windows provider, which was already out of date once custom support landed) and expands WindowsCustomProvider's docs with a usage example showing a FirebaseAppCheckFlutterApi implementation. Regenerates all Pigeon bindings (Dart, Kotlin, Swift, C++) from the updated source. --- .../GeneratedAndroidFirebaseAppCheck.g.kt | 107 +++++++++++++- .../FirebaseAppCheckMessages.g.swift | 131 +++++++++++++++++- .../lib/firebase_app_check.dart | 3 +- .../windows/firebase_app_check_plugin.cpp | 20 +-- .../firebase_app_check/windows/messages.g.cpp | 60 +++++++- .../firebase_app_check/windows/messages.g.h | 44 +++++- ...firebase_app_check_platform_interface.dart | 2 +- .../lib/src/pigeon/messages.pigeon.dart | 84 ++++++++++- .../lib/src/windows_providers.dart | 39 ++++-- .../pigeons/messages.dart | 33 ++++- 10 files changed, 476 insertions(+), 47 deletions(-) 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 7136c50b5d45..e2b417cc4b4f 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 @@ -40,6 +40,36 @@ private object GeneratedAndroidFirebaseAppCheckPigeonUtils { ) } } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is List<*> && b is List<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && a.all { + (b as Map).containsKey(it.key) && + deepEquals(it.value, b[it.key]) + } + } + return a == b + } + } /** @@ -53,12 +83,70 @@ class FlutterError ( override val message: String? = null, val details: Any? = null ) : Throwable() + +/** + * Carries a minted App Check token plus the wall-clock expiry the Firebase + * SDK should associate with it. Returning the expiry alongside the token lets + * backends mint tokens with arbitrary lifetimes (short TTLs for a stricter + * security posture, longer TTLs for fewer round-trips) without the plugin + * hardcoding a refresh window. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class CustomAppCheckToken ( + /** The App Check token string to send with Firebase requests. */ + val token: String, + /** + * Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses + * this to decide when to refresh; a token returned with an expiry in the + * past is treated as immediately expired. + */ + val expireTimeMillis: Long +) + { + companion object { + fun fromList(pigeonVar_list: List): CustomAppCheckToken { + val token = pigeonVar_list[0] as String + val expireTimeMillis = pigeonVar_list[1] as Long + return CustomAppCheckToken(token, expireTimeMillis) + } + } + fun toList(): List { + return listOf( + token, + expireTimeMillis, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is CustomAppCheckToken) { + return false + } + if (this === other) { + return true + } + return GeneratedAndroidFirebaseAppCheckPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { - return super.readValueOfType(type, buffer) + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { + CustomAppCheckToken.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } } override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { - super.writeValue(stream, value) + when (value) { + is CustomAppCheckToken -> { + stream.write(129) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } } } @@ -187,7 +275,16 @@ interface FirebaseAppCheckHostApi { } } } -/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ +/** + * Dart-side handler invoked by the native plugin when the Firebase SDK needs + * a fresh App Check token. Implementations typically call a backend service + * (for example a Cloud Function with `enforceAppCheck: false`) that mints a + * token using the Firebase Admin SDK. The native side awaits the future, + * then hands the token to the Firebase SDK, which attaches it to subsequent + * Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). + * + * Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. + */ class FirebaseAppCheckFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { companion object { /** The codec used by FirebaseAppCheckFlutterApi. */ @@ -195,7 +292,7 @@ class FirebaseAppCheckFlutterApi(private val binaryMessenger: BinaryMessenger, p GeneratedAndroidFirebaseAppCheckPigeonCodec() } } - fun getCustomToken(callback: (Result) -> Unit) + fun getCustomToken(callback: (Result) -> Unit) { val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" val channelName = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$separatedMessageChannelSuffix" @@ -207,7 +304,7 @@ class FirebaseAppCheckFlutterApi(private val binaryMessenger: BinaryMessenger, p } else if (it[0] == null) { callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", ""))) } else { - val output = it[0] as String + val output = it[0] as CustomAppCheckToken callback(Result.success(output)) } } else { 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 6534ba56a79e..ce188243caf8 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 @@ -71,11 +71,129 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } +func deepEqualsFirebaseAppCheckMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case is (Void, Void): + return true + + case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): + return cleanLhsHashable == cleanRhsHashable + + case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): + guard cleanLhsArray.count == cleanRhsArray.count else { return false } + for (index, element) in cleanLhsArray.enumerated() { + if !deepEqualsFirebaseAppCheckMessages(element, cleanRhsArray[index]) { + return false + } + } + return true + + case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } + for (key, cleanLhsValue) in cleanLhsDictionary { + guard cleanRhsDictionary.index(forKey: key) != nil else { return false } + if !deepEqualsFirebaseAppCheckMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + return false + } + } + return true + + default: + // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. + return false + } +} + +func deepHashFirebaseAppCheckMessages(value: Any?, hasher: inout Hasher) { + if let valueList = value as? [AnyHashable] { + for item in valueList { deepHashFirebaseAppCheckMessages(value: item, hasher: &hasher) } + return + } + + if let valueDict = value as? [AnyHashable: AnyHashable] { + for key in valueDict.keys { + hasher.combine(key) + deepHashFirebaseAppCheckMessages(value: valueDict[key]!, hasher: &hasher) + } + return + } + + if let hashableValue = value as? AnyHashable { + hasher.combine(hashableValue.hashValue) + } + + return hasher.combine(String(describing: value)) +} + + + +/// Carries a minted App Check token plus the wall-clock expiry the Firebase +/// SDK should associate with it. Returning the expiry alongside the token lets +/// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter +/// security posture, longer TTLs for fewer round-trips) without the plugin +/// hardcoding a refresh window. +/// +/// Generated class from Pigeon that represents data sent in messages. +struct CustomAppCheckToken: Hashable { + /// The App Check token string to send with Firebase requests. + var token: String + /// Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses + /// this to decide when to refresh; a token returned with an expiry in the + /// past is treated as immediately expired. + var expireTimeMillis: Int64 + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> CustomAppCheckToken? { + let token = pigeonVar_list[0] as! String + let expireTimeMillis = pigeonVar_list[1] as! Int64 + + return CustomAppCheckToken( + token: token, + expireTimeMillis: expireTimeMillis + ) + } + func toList() -> [Any?] { + return [ + token, + expireTimeMillis, + ] + } + static func == (lhs: CustomAppCheckToken, rhs: CustomAppCheckToken) -> Bool { + return deepEqualsFirebaseAppCheckMessages(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashFirebaseAppCheckMessages(value: toList(), hasher: &hasher) + } +} private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return CustomAppCheckToken.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } } private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? CustomAppCheckToken { + super.writeByte(129) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } } private class FirebaseAppCheckMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { @@ -201,9 +319,16 @@ class FirebaseAppCheckHostApiSetup { } } } +/// Dart-side handler invoked by the native plugin when the Firebase SDK needs +/// a fresh App Check token. Implementations typically call a backend service +/// (for example a Cloud Function with `enforceAppCheck: false`) that mints a +/// token using the Firebase Admin SDK. The native side awaits the future, +/// then hands the token to the Firebase SDK, which attaches it to subsequent +/// Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). +/// /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol FirebaseAppCheckFlutterApiProtocol { - func getCustomToken(completion: @escaping (Result) -> Void) + func getCustomToken(completion: @escaping (Result) -> Void) } class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -215,7 +340,7 @@ class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { var codec: FirebaseAppCheckMessagesPigeonCodec { return FirebaseAppCheckMessagesPigeonCodec.shared } - func getCustomToken(completion: @escaping (Result) -> Void) { + func getCustomToken(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) channel.sendMessage(nil) { response in @@ -231,7 +356,7 @@ class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { } else if listResponse[0] == nil { completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { - let result = listResponse[0] as! String + let result = listResponse[0] as! CustomAppCheckToken completion(.success(result)) } } 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 68cef9f227d0..53589fad47db 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 @@ -25,7 +25,8 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor WindowsAppCheckProvider, WindowsDebugProvider, WindowsCustomProvider, - FirebaseAppCheckFlutterApi; + FirebaseAppCheckFlutterApi, + CustomAppCheckToken; export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' show FirebaseException; diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp index 0b7a7ec4332c..8e04926f3e52 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -99,9 +98,11 @@ class TokenStreamHandler std::unique_ptr listener_; }; -// FlutterCustomAppCheckProvider — calls into Dart via the FlutterApi and +// FlutterCustomAppCheckProvider calls into Dart via the FlutterApi and // completes the Firebase C++ SDK callback asynchronously when Dart returns a -// token (or an error). +// token (or an error). The Dart handler returns the token together with its +// expiry, so the C++ SDK can cache for the exact lifetime the backend minted +// rather than a hardcoded refresh window. FlutterCustomAppCheckProvider::FlutterCustomAppCheckProvider( flutter::BinaryMessenger* binary_messenger) : flutter_api_( @@ -116,17 +117,10 @@ void FlutterCustomAppCheckProvider::GetToken( const std::string&)>>(std::move(completion_callback)); flutter_api_->GetCustomToken( - [completion](const std::string& token) { + [completion](const CustomAppCheckToken& dart_token) { firebase::app_check::AppCheckToken result_token; - result_token.token = token; - // Set expiry to 55 minutes from now (server mints 1-hour tokens; - // 5-minute buffer avoids using a nearly-expired token). - result_token.expire_time_millis = - static_cast( - std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count()) + - 55LL * 60LL * 1000LL; + result_token.token = dart_token.token(); + result_token.expire_time_millis = dart_token.expire_time_millis(); (*completion)(result_token, firebase::app_check::kAppCheckErrorNone, ""); }, 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 bb380c01c196..4bfbb9ce703e 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 @@ -31,18 +31,72 @@ FlutterError CreateConnectionError(const std::string channel_name) { EncodableValue("")); } +// CustomAppCheckToken + +CustomAppCheckToken::CustomAppCheckToken( + const std::string& token, + int64_t expire_time_millis) + : token_(token), + expire_time_millis_(expire_time_millis) {} + +const std::string& CustomAppCheckToken::token() const { + return token_; +} + +void CustomAppCheckToken::set_token(std::string_view value_arg) { + token_ = value_arg; +} + + +int64_t CustomAppCheckToken::expire_time_millis() const { + return expire_time_millis_; +} + +void CustomAppCheckToken::set_expire_time_millis(int64_t value_arg) { + expire_time_millis_ = value_arg; +} + + +EncodableList CustomAppCheckToken::ToEncodableList() const { + EncodableList list; + list.reserve(2); + list.push_back(EncodableValue(token_)); + list.push_back(EncodableValue(expire_time_millis_)); + return list; +} + +CustomAppCheckToken CustomAppCheckToken::FromEncodableList(const EncodableList& list) { + CustomAppCheckToken decoded( + std::get(list[0]), + std::get(list[1])); + return decoded; +} + PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( uint8_t type, flutter::ByteStreamReader* stream) const { - return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + switch (type) { + case 129: { + return CustomEncodableValue(CustomAppCheckToken::FromEncodableList(std::get(ReadValue(stream)))); + } + default: + return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); + } } void PigeonInternalCodecSerializer::WriteValue( const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + if (custom_value->type() == typeid(CustomAppCheckToken)) { + stream->WriteByte(129); + WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + return; + } + } flutter::StandardCodecSerializer::WriteValue(value, stream); } @@ -267,7 +321,7 @@ const flutter::StandardMessageCodec& FirebaseAppCheckFlutterApi::GetCodec() { } void FirebaseAppCheckFlutterApi::GetCustomToken( - std::function&& on_success, + std::function&& on_success, std::function&& on_error) { const std::string channel_name = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken" + message_channel_suffix_; BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); @@ -280,7 +334,7 @@ void FirebaseAppCheckFlutterApi::GetCustomToken( if (list_return_value->size() > 1) { on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); } else { - const auto& return_value = std::get(list_return_value->at(0)); + const auto& return_value = std::any_cast(std::get(list_return_value->at(0))); on_success(return_value); } } else { 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 292f60d0a0d8..1f65268f446c 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 @@ -61,6 +61,41 @@ template class ErrorOr { +// Carries a minted App Check token plus the wall-clock expiry the Firebase +// SDK should associate with it. Returning the expiry alongside the token lets +// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter +// security posture, longer TTLs for fewer round-trips) without the plugin +// hardcoding a refresh window. +// +// Generated class from Pigeon that represents data sent in messages. +class CustomAppCheckToken { + public: + // Constructs an object setting all fields. + explicit CustomAppCheckToken( + const std::string& token, + int64_t expire_time_millis); + + // The App Check token string to send with Firebase requests. + const std::string& token() const; + void set_token(std::string_view value_arg); + + // Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses + // this to decide when to refresh; a token returned with an expiry in the + // past is treated as immediately expired. + int64_t expire_time_millis() const; + void set_expire_time_millis(int64_t value_arg); + + private: + static CustomAppCheckToken FromEncodableList(const flutter::EncodableList& list); + flutter::EncodableList ToEncodableList() const; + friend class FirebaseAppCheckHostApi; + friend class FirebaseAppCheckFlutterApi; + friend class PigeonInternalCodecSerializer; + std::string token_; + int64_t expire_time_millis_; +}; + + class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); @@ -121,6 +156,13 @@ class FirebaseAppCheckHostApi { protected: FirebaseAppCheckHostApi() = default; }; +// Dart-side handler invoked by the native plugin when the Firebase SDK needs +// a fresh App Check token. Implementations typically call a backend service +// (for example a Cloud Function with `enforceAppCheck: false`) that mints a +// token using the Firebase Admin SDK. The native side awaits the future, +// then hands the token to the Firebase SDK, which attaches it to subsequent +// Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). +// // Generated class from Pigeon that represents Flutter messages that can be called from C++. class FirebaseAppCheckFlutterApi { public: @@ -130,7 +172,7 @@ class FirebaseAppCheckFlutterApi { const std::string& message_channel_suffix); static const flutter::StandardMessageCodec& GetCodec(); void GetCustomToken( - std::function&& on_success, + std::function&& on_success, std::function&& on_error); private: flutter::BinaryMessenger* binary_messenger_; 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 27a14c5a368b..fa9367cbc561 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 @@ -11,4 +11,4 @@ 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'; export 'src/windows_providers.dart'; -export 'src/pigeon/messages.pigeon.dart' show FirebaseAppCheckFlutterApi; +export 'src/pigeon/messages.pigeon.dart' show FirebaseAppCheckFlutterApi, CustomAppCheckToken; 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 89868148521f..08f66d18b239 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 @@ -27,6 +27,75 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); + } + return a == b; +} + + +/// Carries a minted App Check token plus the wall-clock expiry the Firebase +/// SDK should associate with it. Returning the expiry alongside the token lets +/// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter +/// security posture, longer TTLs for fewer round-trips) without the plugin +/// hardcoding a refresh window. +class CustomAppCheckToken { + CustomAppCheckToken({ + required this.token, + required this.expireTimeMillis, + }); + + /// The App Check token string to send with Firebase requests. + String token; + + /// Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses + /// this to decide when to refresh; a token returned with an expiry in the + /// past is treated as immediately expired. + int expireTimeMillis; + + List _toList() { + return [ + token, + expireTimeMillis, + ]; + } + + Object encode() { + return _toList(); } + + static CustomAppCheckToken decode(Object result) { + result as List; + return CustomAppCheckToken( + token: result[0]! as String, + expireTimeMillis: result[1]! as int, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! CustomAppCheckToken || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} class _PigeonCodec extends StandardMessageCodec { @@ -36,6 +105,9 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); + } else if (value is CustomAppCheckToken) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); } else { super.writeValue(buffer, value); } @@ -44,6 +116,8 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { + case 129: + return CustomAppCheckToken.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); } @@ -189,10 +263,16 @@ class FirebaseAppCheckHostApi { } } +/// Dart-side handler invoked by the native plugin when the Firebase SDK needs +/// a fresh App Check token. Implementations typically call a backend service +/// (for example a Cloud Function with `enforceAppCheck: false`) that mints a +/// token using the Firebase Admin SDK. The native side awaits the future, +/// then hands the token to the Firebase SDK, which attaches it to subsequent +/// Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). abstract class FirebaseAppCheckFlutterApi { static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - Future getCustomToken(); + Future getCustomToken(); static void setUp(FirebaseAppCheckFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; @@ -205,7 +285,7 @@ abstract class FirebaseAppCheckFlutterApi { } else { pigeonVar_channel.setMessageHandler((Object? message) async { try { - final String output = await api.getCustomToken(); + final CustomAppCheckToken output = await api.getCustomToken(); return wrapResponse(result: output); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart index b5c4e78b3ac4..be6f58d980f9 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart @@ -4,9 +4,10 @@ /// Base class for Windows App Check providers. /// -/// On Windows, only the [WindowsDebugProvider] is supported. The Firebase C++ -/// SDK does not support platform attestation providers (such as Play Integrity -/// or DeviceCheck) on desktop platforms. +/// The Firebase C++ SDK does not ship native platform attestation providers +/// (such as Play Integrity or DeviceCheck) on desktop, so Windows supports +/// [WindowsDebugProvider] for development and [WindowsCustomProvider] for +/// production builds that mint tokens via a backend. abstract class WindowsAppCheckProvider { final String type; const WindowsAppCheckProvider(this.type); @@ -15,24 +16,38 @@ abstract class WindowsAppCheckProvider { /// Custom provider for Windows production builds. /// /// When activated, the Windows C++ plugin registers a custom -/// [AppCheckProvider] that calls into Dart via a Pigeon FlutterApi each time -/// the Firebase SDK needs a fresh token. The Dart handler is expected to call -/// a server-side Cloud Function (e.g. `getWindowsAppCheckToken`) that mints a -/// valid App Check token using the Firebase Admin SDK and returns it. +/// `AppCheckProvider` that calls into Dart via a Pigeon `FlutterApi` each time +/// the Firebase SDK needs a fresh App Check token. The Dart handler is +/// expected to call a backend service (typically a Cloud Function with +/// `enforceAppCheck: false`) that mints a valid App Check token using the +/// Firebase Admin SDK, then return both the token and its expiry. +/// +/// Register the Dart token handler before any Firebase operations that require +/// App Check, alongside `FirebaseAppCheck.instance.activate`: /// -/// Register the Dart token handler before any Firestore operations: /// ```dart -/// FirebaseAppCheckFlutterApi.setUp(myHandler); -/// ``` +/// FirebaseAppCheckFlutterApi.setUp(MyWindowsTokenHandler()); /// -/// **Do not use [WindowsDebugProvider] in production builds.** +/// class MyWindowsTokenHandler implements FirebaseAppCheckFlutterApi { +/// @override +/// Future getCustomToken() async { +/// // Call your backend, e.g. a callable Cloud Function that uses +/// // admin.appCheck().createToken(windowsAppId). +/// final response = await myBackend.mintAppCheckToken(); +/// return CustomAppCheckToken( +/// token: response.token, +/// expireTimeMillis: response.expireTimeMillis, +/// ); +/// } +/// } +/// ``` class WindowsCustomProvider extends WindowsAppCheckProvider { const WindowsCustomProvider() : super('custom'); } /// Debug provider for Windows. /// -/// This is the **only** provider available on Windows. Unlike mobile platforms, +/// Intended for development and local testing only. Unlike mobile platforms, /// the desktop C++ SDK does **not** auto-generate a debug token. You must /// supply one explicitly. /// 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 ffdd7dcfa199..5c05a2858eda 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 @@ -48,13 +48,34 @@ abstract class FirebaseAppCheckHostApi { String getLimitedUseAppCheckToken(String appName); } -// Dart-side handler invoked by C++ when the Firebase SDK needs a fresh App -// Check token on Windows. Implementations call getWindowsAppCheckToken (a -// Cloud Function with enforceAppCheck:false) and return the minted token -// string. The C++ side blocks until the Future resolves, then hands the token -// to the Firebase SDK, which attaches it to every subsequent Firestore request. +/// Carries a minted App Check token plus the wall-clock expiry the Firebase +/// SDK should associate with it. Returning the expiry alongside the token lets +/// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter +/// security posture, longer TTLs for fewer round-trips) without the plugin +/// hardcoding a refresh window. +class CustomAppCheckToken { + CustomAppCheckToken({ + required this.token, + required this.expireTimeMillis, + }); + + /// The App Check token string to send with Firebase requests. + final String token; + + /// Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses + /// this to decide when to refresh; a token returned with an expiry in the + /// past is treated as immediately expired. + final int expireTimeMillis; +} + +/// Dart-side handler invoked by the native plugin when the Firebase SDK needs +/// a fresh App Check token. Implementations typically call a backend service +/// (for example a Cloud Function with `enforceAppCheck: false`) that mints a +/// token using the Firebase Admin SDK. The native side awaits the future, +/// then hands the token to the Firebase SDK, which attaches it to subsequent +/// Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). @FlutterApi() abstract class FirebaseAppCheckFlutterApi { @async - String getCustomToken(); + CustomAppCheckToken getCustomToken(); } From 79e5de394ea7fbd8be06be3d4d2bc4649487a099 Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Tue, 14 Apr 2026 12:15:02 -0400 Subject: [PATCH 3/6] test(app-check-windows): Cover activate payload and FlutterApi contract Exercises the Windows activate() payload for WindowsCustomProvider and WindowsDebugProvider (with and without an explicit debug token), plus the FirebaseAppCheckFlutterApi success and PlatformException envelopes. --- ...ethod_channel_firebase_app_check_test.dart | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) 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 5f215cec19fc..c6ca01b17b09 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 @@ -3,7 +3,10 @@ // found in the LICENSE file. import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart'; +import 'package:firebase_app_check_platform_interface/src/pigeon/messages.pigeon.dart'; import 'package:firebase_core/firebase_core.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import '../mock.dart'; @@ -44,5 +47,168 @@ void main() { expect(appCheck.setInitialValues(), appCheck); }); }); + + group('activate() on Windows', () { + late BasicMessageChannel activateChannel; + late List activateMessages; + + setUp(() { + debugDefaultTargetPlatformOverride = TargetPlatform.windows; + activateChannel = const BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate', + FirebaseAppCheckHostApi.pigeonChannelCodec, + ); + activateMessages = []; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockDecodedMessageHandler(activateChannel, + (Object? message) async { + activateMessages.add(message); + return []; + }); + }); + + tearDown(() { + debugDefaultTargetPlatformOverride = null; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockDecodedMessageHandler(activateChannel, null); + }); + + test('forwards WindowsCustomProvider over Pigeon', () async { + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + await appCheck.activate( + providerWindows: const WindowsCustomProvider(), + ); + + // Android/Apple slots carry method-channel defaults even on Windows. + expect(activateMessages, hasLength(1)); + expect(activateMessages.single, [ + 'secondaryApp', + 'playIntegrity', + 'deviceCheck', + null, + 'custom', + ]); + }); + + test( + 'forwards WindowsDebugProvider with an explicit debug token over Pigeon', + () async { + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + await appCheck.activate( + providerWindows: const WindowsDebugProvider( + debugToken: 'debug-token', + ), + ); + + expect(activateMessages, hasLength(1)); + expect(activateMessages.single, [ + 'secondaryApp', + 'playIntegrity', + 'deviceCheck', + 'debug-token', + 'debug', + ]); + }); + + test( + 'forwards WindowsDebugProvider with no explicit token as null ' + '(env-var fallback path)', () async { + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + // Null debugToken triggers the native APP_CHECK_DEBUG_TOKEN fallback. + await appCheck.activate( + providerWindows: const WindowsDebugProvider(), + ); + + expect(activateMessages, hasLength(1)); + expect(activateMessages.single, [ + 'secondaryApp', + 'playIntegrity', + 'deviceCheck', + null, + 'debug', + ]); + }); + }); + }); + + group('$FirebaseAppCheckFlutterApi', () { + const BasicMessageChannel flutterApiChannel = + BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken', + FirebaseAppCheckFlutterApi.pigeonChannelCodec, + ); + + tearDown(() { + FirebaseAppCheckFlutterApi.setUp(null); + }); + + test('returns CustomAppCheckToken in a success envelope', () async { + final token = CustomAppCheckToken( + token: 'app-check-token', + expireTimeMillis: 1735689600000, + ); + FirebaseAppCheckFlutterApi.setUp( + _TestFirebaseAppCheckFlutterApi( + onGetCustomToken: () async => token, + ), + ); + + final replyData = await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger + .handlePlatformMessage( + flutterApiChannel.name, + flutterApiChannel.codec.encodeMessage(null), + null, + ); + final reply = + flutterApiChannel.codec.decodeMessage(replyData) as List?; + + expect(reply, [token]); + }); + + test('returns a PlatformException envelope when the handler throws', + () async { + FirebaseAppCheckFlutterApi.setUp( + _TestFirebaseAppCheckFlutterApi( + onGetCustomToken: () async { + throw PlatformException( + code: 'token-error', + message: 'Failed to mint App Check token', + details: {'source': 'test'}, + ); + }, + ), + ); + + final replyData = await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger + .handlePlatformMessage( + flutterApiChannel.name, + flutterApiChannel.codec.encodeMessage(null), + null, + ); + final reply = + flutterApiChannel.codec.decodeMessage(replyData) as List?; + + expect(reply, [ + 'token-error', + 'Failed to mint App Check token', + {'source': 'test'}, + ]); + }); }); } + +class _TestFirebaseAppCheckFlutterApi implements FirebaseAppCheckFlutterApi { + _TestFirebaseAppCheckFlutterApi({ + required this.onGetCustomToken, + }); + + final Future Function() onGetCustomToken; + + @override + Future getCustomToken() => onGetCustomToken(); +} From f893bccdeb6c59a11e64c0610fda7ee8f1726977 Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Wed, 15 Apr 2026 09:38:24 -0400 Subject: [PATCH 4/6] ran melos format-ci --- .../FirebaseAppCheckMessages.g.swift | 160 ++++-- .../windows/firebase_app_check_plugin.cpp | 9 +- .../firebase_app_check/windows/messages.g.cpp | 458 ++++++++++-------- .../firebase_app_check/windows/messages.g.h | 95 ++-- ...firebase_app_check_platform_interface.dart | 3 +- .../lib/src/pigeon/messages.pigeon.dart | 115 +++-- .../pigeons/messages.dart | 2 + 7 files changed, 505 insertions(+), 337 deletions(-) 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 ce188243caf8..0db996a4e4aa 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 @@ -27,13 +27,12 @@ final class PigeonError: Error { } var localizedDescription: String { - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" } } private func wrapResult(_ result: Any?) -> [Any?] { - return [result] + [result] } private func wrapError(_ error: Any) -> [Any?] { @@ -59,11 +58,15 @@ private func wrapError(_ error: Any) -> [Any?] { } private func createConnectionError(withChannelName channelName: String) -> PigeonError { - return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") + PigeonError( + code: "channel-error", + message: "Unable to establish connection on channel: '\(channelName)'.", + details: "" + ) } private func isNullish(_ value: Any?) -> Bool { - return value is NSNull || value == nil + value is NSNull || value == nil } private func nilOrValue(_ value: Any?) -> T? { @@ -107,19 +110,22 @@ func deepEqualsFirebaseAppCheckMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { return true default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. + // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be + // untrue. return false } } func deepHashFirebaseAppCheckMessages(value: Any?, hasher: inout Hasher) { if let valueList = value as? [AnyHashable] { - for item in valueList { deepHashFirebaseAppCheckMessages(value: item, hasher: &hasher) } - return + for item in valueList { + deepHashFirebaseAppCheckMessages(value: item, hasher: &hasher) + } + return } if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { + for key in valueDict.keys { hasher.combine(key) deepHashFirebaseAppCheckMessages(value: valueDict[key]!, hasher: &hasher) } @@ -133,8 +139,6 @@ func deepHashFirebaseAppCheckMessages(value: Any?, hasher: inout Hasher) { return hasher.combine(String(describing: value)) } - - /// Carries a minted App Check token plus the wall-clock expiry the Firebase /// SDK should associate with it. Returning the expiry alongside the token lets /// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter @@ -150,7 +154,6 @@ struct CustomAppCheckToken: Hashable { /// past is treated as immediately expired. var expireTimeMillis: Int64 - // swift-format-ignore: AlwaysUseLowerCamelCase static func fromList(_ pigeonVar_list: [Any?]) -> CustomAppCheckToken? { let token = pigeonVar_list[0] as! String @@ -161,14 +164,18 @@ struct CustomAppCheckToken: Hashable { expireTimeMillis: expireTimeMillis ) } + func toList() -> [Any?] { - return [ + [ token, expireTimeMillis, ] } + static func == (lhs: CustomAppCheckToken, rhs: CustomAppCheckToken) -> Bool { - return deepEqualsFirebaseAppCheckMessages(lhs.toList(), rhs.toList()) } + deepEqualsFirebaseAppCheckMessages(lhs.toList(), rhs.toList()) + } + func hash(into hasher: inout Hasher) { deepHashFirebaseAppCheckMessages(value: toList(), hasher: &hasher) } @@ -178,7 +185,7 @@ private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader { override func readValue(ofType type: UInt8) -> Any? { switch type { case 129: - return CustomAppCheckToken.fromList(self.readValue() as! [Any?]) + return CustomAppCheckToken.fromList(readValue() as! [Any?]) default: return super.readValue(ofType: type) } @@ -198,36 +205,52 @@ private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter { private class FirebaseAppCheckMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { override func reader(with data: Data) -> FlutterStandardReader { - return FirebaseAppCheckMessagesPigeonCodecReader(data: data) + FirebaseAppCheckMessagesPigeonCodecReader(data: data) } override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return FirebaseAppCheckMessagesPigeonCodecWriter(data: data) + 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?, windowsProvider: 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?, windowsProvider: 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 = api { + let activateChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let api { activateChannel.setMessageHandler { message, reply in let args = message as! [Any?] let appNameArg = args[0] as! String @@ -235,11 +258,17 @@ class FirebaseAppCheckHostApiSetup { let appleProviderArg: String? = nilOrValue(args[2]) let debugTokenArg: String? = nilOrValue(args[3]) let windowsProviderArg: String? = nilOrValue(args[4]) - api.activate(appName: appNameArg, androidProvider: androidProviderArg, appleProvider: appleProviderArg, debugToken: debugTokenArg, windowsProvider: windowsProviderArg) { result in + api.activate( + appName: appNameArg, + androidProvider: androidProviderArg, + appleProvider: appleProviderArg, + debugToken: debugTokenArg, + windowsProvider: windowsProviderArg + ) { result in switch result { case .success: reply(wrapResult(nil)) - case .failure(let error): + case let .failure(error): reply(wrapError(error)) } } @@ -247,17 +276,21 @@ 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 = api { + let getTokenChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let 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 .success(let res): + case let .success(res): reply(wrapResult(res)) - case .failure(let error): + case let .failure(error): reply(wrapError(error)) } } @@ -265,17 +298,24 @@ 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 = api { + let setTokenAutoRefreshEnabledChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let 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 .failure(let error): + case let .failure(error): reply(wrapError(error)) } } @@ -283,16 +323,20 @@ 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 = api { + let registerTokenListenerChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let 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 .success(let res): + case let .success(res): reply(wrapResult(res)) - case .failure(let error): + case let .failure(error): reply(wrapError(error)) } } @@ -300,16 +344,20 @@ 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 = api { + let getLimitedUseAppCheckTokenChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken\(channelSuffix)", + binaryMessenger: binaryMessenger, + codec: codec + ) + if let 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 .success(let res): + case let .success(res): reply(wrapResult(res)) - case .failure(let error): + case let .failure(error): reply(wrapError(error)) } } @@ -319,6 +367,7 @@ class FirebaseAppCheckHostApiSetup { } } } + /// Dart-side handler invoked by the native plugin when the Firebase SDK needs /// a fresh App Check token. Implementations typically call a backend service /// (for example a Cloud Function with `enforceAppCheck: false`) that mints a @@ -330,6 +379,7 @@ class FirebaseAppCheckHostApiSetup { protocol FirebaseAppCheckFlutterApiProtocol { func getCustomToken(completion: @escaping (Result) -> Void) } + class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { private let binaryMessenger: FlutterBinaryMessenger private let messageChannelSuffix: String @@ -337,12 +387,18 @@ class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { self.binaryMessenger = binaryMessenger self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" } + var codec: FirebaseAppCheckMessagesPigeonCodec { - return FirebaseAppCheckMessagesPigeonCodec.shared + FirebaseAppCheckMessagesPigeonCodec.shared } + func getCustomToken(completion: @escaping (Result) -> Void) { - let channelName: String = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken\(messageChannelSuffix)" - let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + let channelName = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel( + name: channelName, + binaryMessenger: binaryMessenger, + codec: codec + ) channel.sendMessage(nil) { response in guard let listResponse = response as? [Any?] else { completion(.failure(createConnectionError(withChannelName: channelName))) @@ -354,7 +410,11 @@ class FirebaseAppCheckFlutterApi: FirebaseAppCheckFlutterApiProtocol { let details: String? = nilOrValue(listResponse[2]) completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { - completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) + completion(.failure(PigeonError( + code: "null-error", + message: "Flutter api returned null value for non-null return value.", + details: "" + ))) } else { let result = listResponse[0] as! CustomAppCheckToken completion(.success(result)) diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp index 8e04926f3e52..789a1dc33d81 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp @@ -112,9 +112,9 @@ void FlutterCustomAppCheckProvider::GetToken( std::function completion_callback) { - auto completion = std::make_shared< - std::function>(std::move(completion_callback)); + auto completion = std::make_shared>( + std::move(completion_callback)); flutter_api_->GetCustomToken( [completion](const CustomAppCheckToken& dart_token) { @@ -216,8 +216,7 @@ void FirebaseAppCheckPlugin::Activate( std::function reply)> result) { if (windows_provider != nullptr && *windows_provider == "custom") { custom_provider_factory_ = - std::make_unique( - binaryMessenger); + std::make_unique(binaryMessenger); AppCheck::SetAppCheckProviderFactory(custom_provider_factory_.get()); } else { DebugAppCheckProviderFactory* factory = 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 4bfbb9ce703e..4682f116d498 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 @@ -33,21 +33,16 @@ FlutterError CreateConnectionError(const std::string channel_name) { // CustomAppCheckToken -CustomAppCheckToken::CustomAppCheckToken( - const std::string& token, - int64_t expire_time_millis) - : token_(token), - expire_time_millis_(expire_time_millis) {} +CustomAppCheckToken::CustomAppCheckToken(const std::string& token, + int64_t expire_time_millis) + : token_(token), expire_time_millis_(expire_time_millis) {} -const std::string& CustomAppCheckToken::token() const { - return token_; -} +const std::string& CustomAppCheckToken::token() const { return token_; } void CustomAppCheckToken::set_token(std::string_view value_arg) { token_ = value_arg; } - int64_t CustomAppCheckToken::expire_time_millis() const { return expire_time_millis_; } @@ -56,7 +51,6 @@ void CustomAppCheckToken::set_expire_time_millis(int64_t value_arg) { expire_time_millis_ = value_arg; } - EncodableList CustomAppCheckToken::ToEncodableList() const { EncodableList list; list.reserve(2); @@ -65,35 +59,37 @@ EncodableList CustomAppCheckToken::ToEncodableList() const { return list; } -CustomAppCheckToken CustomAppCheckToken::FromEncodableList(const EncodableList& list) { - CustomAppCheckToken decoded( - std::get(list[0]), - std::get(list[1])); +CustomAppCheckToken CustomAppCheckToken::FromEncodableList( + const EncodableList& list) { + CustomAppCheckToken decoded(std::get(list[0]), + std::get(list[1])); return decoded; } - PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, - flutter::ByteStreamReader* stream) const { + uint8_t type, flutter::ByteStreamReader* stream) const { switch (type) { case 129: { - return CustomEncodableValue(CustomAppCheckToken::FromEncodableList(std::get(ReadValue(stream)))); - } + return CustomEncodableValue(CustomAppCheckToken::FromEncodableList( + std::get(ReadValue(stream)))); + } default: return flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } + } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, - flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + const EncodableValue& value, flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = + std::get_if(&value)) { if (custom_value->type() == typeid(CustomAppCheckToken)) { stream->WriteByte(129); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } } @@ -102,244 +98,320 @@ void PigeonInternalCodecSerializer::WriteValue( /// The codec used by FirebaseAppCheckHostApi. const flutter::StandardMessageCodec& FirebaseAppCheckHostApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); + 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); - const auto& encodable_windows_provider_arg = args.at(4); - const auto* windows_provider_arg = std::get_if(&encodable_windows_provider_arg); - api->Activate(app_name_arg, android_provider_arg, apple_provider_arg, debug_token_arg, windows_provider_arg, [reply](std::optional&& output) { - if (output.has_value()) { - reply(WrapError(output.value())); - return; + 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_windows_provider_arg = args.at(4); + const auto* windows_provider_arg = + std::get_if(&encodable_windows_provider_arg); + api->Activate(app_name_arg, android_provider_arg, + apple_provider_arg, debug_token_arg, + windows_provider_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())); } - 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()); + 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())); } - 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; + 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())); } - 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; + 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())); } - 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; + 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())); } - 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()}); } -// Generated class from Pigeon that represents Flutter messages that can be called from C++. -FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi(flutter::BinaryMessenger* binary_messenger) - : binary_messenger_(binary_messenger), - message_channel_suffix_("") {} +// Generated class from Pigeon that represents Flutter messages that can be +// called from C++. +FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi( + flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), message_channel_suffix_("") {} FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi( - flutter::BinaryMessenger* binary_messenger, - const std::string& message_channel_suffix) - : binary_messenger_(binary_messenger), - message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : "") {} const flutter::StandardMessageCodec& FirebaseAppCheckFlutterApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); + return flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } void FirebaseAppCheckFlutterApi::GetCustomToken( - std::function&& on_success, - std::function&& on_error) { - const std::string channel_name = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken" + message_channel_suffix_; + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckFlutterApi.getCustomToken" + + message_channel_suffix_; BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { - std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { + std::unique_ptr response = + GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; - const auto* list_return_value = std::get_if(&encodable_return_value); + const auto* list_return_value = + std::get_if(&encodable_return_value); if (list_return_value) { if (list_return_value->size() > 1) { - on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + on_error(FlutterError(std::get(list_return_value->at(0)), + std::get(list_return_value->at(1)), + list_return_value->at(2))); } else { - const auto& return_value = std::any_cast(std::get(list_return_value->at(0))); + const auto& return_value = std::any_cast( + std::get(list_return_value->at(0))); on_success(return_value); } } else { on_error(CreateConnectionError(channel_name)); - } + } }); } 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 1f65268f446c..f3ac230526eb 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 @@ -17,17 +17,16 @@ 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_; } @@ -39,7 +38,8 @@ class FlutterError { flutter::EncodableValue details_; }; -template class ErrorOr { +template +class ErrorOr { public: ErrorOr(const T& rhs) : v_(rhs) {} ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} @@ -59,8 +59,6 @@ template class ErrorOr { std::variant v_; }; - - // Carries a minted App Check token plus the wall-clock expiry the Firebase // SDK should associate with it. Returning the expiry alongside the token lets // backends mint tokens with arbitrary lifetimes (short TTLs for a stricter @@ -71,9 +69,8 @@ template class ErrorOr { class CustomAppCheckToken { public: // Constructs an object setting all fields. - explicit CustomAppCheckToken( - const std::string& token, - int64_t expire_time_millis); + explicit CustomAppCheckToken(const std::string& token, + int64_t expire_time_millis); // The App Check token string to send with Firebase requests. const std::string& token() const; @@ -86,7 +83,8 @@ class CustomAppCheckToken { void set_expire_time_millis(int64_t value_arg); private: - static CustomAppCheckToken FromEncodableList(const flutter::EncodableList& list); + static CustomAppCheckToken FromEncodableList( + const flutter::EncodableList& list); flutter::EncodableList ToEncodableList() const; friend class FirebaseAppCheckHostApi; friend class FirebaseAppCheckFlutterApi; @@ -95,7 +93,6 @@ class CustomAppCheckToken { int64_t expire_time_millis_; }; - class PigeonInternalCodecSerializer : public flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); @@ -104,55 +101,52 @@ 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; + 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, - const std::string* windows_provider, - 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* windows_provider, + 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); + // 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; }; @@ -163,17 +157,18 @@ class FirebaseAppCheckHostApi { // then hands the token to the Firebase SDK, which attaches it to subsequent // Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). // -// Generated class from Pigeon that represents Flutter messages that can be called from C++. +// Generated class from Pigeon that represents Flutter messages that can be +// called from C++. class FirebaseAppCheckFlutterApi { public: FirebaseAppCheckFlutterApi(flutter::BinaryMessenger* binary_messenger); - FirebaseAppCheckFlutterApi( - flutter::BinaryMessenger* binary_messenger, - const std::string& message_channel_suffix); + FirebaseAppCheckFlutterApi(flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); static const flutter::StandardMessageCodec& GetCodec(); void GetCustomToken( - std::function&& on_success, - std::function&& on_error); + std::function&& on_success, + std::function&& on_error); + private: flutter::BinaryMessenger* binary_messenger_; std::string message_channel_suffix_; 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 fa9367cbc561..b376615a7eb9 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 @@ -11,4 +11,5 @@ 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'; export 'src/windows_providers.dart'; -export 'src/pigeon/messages.pigeon.dart' show FirebaseAppCheckFlutterApi, CustomAppCheckToken; +export 'src/pigeon/messages.pigeon.dart' + show FirebaseAppCheckFlutterApi, CustomAppCheckToken; 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 08f66d18b239..8872f9a95ab5 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 @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. // Autogenerated from Pigeon (v25.3.2), 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: 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, require_trailing_commas, parameter_assignments import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; @@ -18,7 +18,8 @@ PlatformException _createConnectionError(String channelName) { ); } -List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { +List wrapResponse( + {Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; } @@ -27,21 +28,22 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } + bool _deepEquals(Object? a, Object? b) { if (a is List && b is List) { return a.length == b.length && a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); } if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); + return a.length == b.length && + a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); } return a == b; } - /// Carries a minted App Check token plus the wall-clock expiry the Firebase /// SDK should associate with it. Returning the expiry alongside the token lets /// backends mint tokens with arbitrary lifetimes (short TTLs for a stricter @@ -69,7 +71,8 @@ class CustomAppCheckToken { } Object encode() { - return _toList(); } + return _toList(); + } static CustomAppCheckToken decode(Object result) { result as List; @@ -93,11 +96,9 @@ class CustomAppCheckToken { @override // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; + int get hashCode => Object.hashAll(_toList()); } - class _PigeonCodec extends StandardMessageCodec { const _PigeonCodec(); @override @@ -105,7 +106,7 @@ class _PigeonCodec extends StandardMessageCodec { if (value is int) { buffer.putUint8(4); buffer.putInt64(value); - } else if (value is CustomAppCheckToken) { + } else if (value is CustomAppCheckToken) { buffer.putUint8(129); writeValue(buffer, value.encode()); } else { @@ -116,7 +117,7 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return CustomAppCheckToken.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -128,23 +129,39 @@ 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, String? windowsProvider) 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? windowsProvider) async { + final String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, androidProvider, appleProvider, debugToken, windowsProvider]); + final Future pigeonVar_sendFuture = pigeonVar_channel + .send([ + appName, + androidProvider, + appleProvider, + debugToken, + windowsProvider + ]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -161,13 +178,16 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, forceRefresh]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, forceRefresh]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -183,14 +203,18 @@ class FirebaseAppCheckHostApi { } } - 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 String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName, isTokenAutoRefreshEnabled]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -207,13 +231,16 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -235,13 +262,16 @@ class FirebaseAppCheckHostApi { } 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 String pigeonVar_channelName = + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([appName]); + final Future pigeonVar_sendFuture = + pigeonVar_channel.send([appName]); final List? pigeonVar_replyList = await pigeonVar_sendFuture as List?; if (pigeonVar_replyList == null) { @@ -274,11 +304,19 @@ abstract class FirebaseAppCheckFlutterApi { Future getCustomToken(); - static void setUp(FirebaseAppCheckFlutterApi? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { - messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + static void setUp( + FirebaseAppCheckFlutterApi? api, { + BinaryMessenger? binaryMessenger, + String messageChannelSuffix = '', + }) { + messageChannelSuffix = + messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$messageChannelSuffix', pigeonChannelCodec, + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$messageChannelSuffix', + pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); @@ -289,8 +327,9 @@ abstract class FirebaseAppCheckFlutterApi { return wrapResponse(result: output); } on PlatformException catch (e) { return wrapResponse(error: e); - } catch (e) { - return wrapResponse(error: PlatformException(code: 'error', message: e.toString())); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); } }); } 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 5c05a2858eda..67a24783a5c7 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 @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// ignore_for_file: one_member_abstracts + import 'package:pigeon/pigeon.dart'; @ConfigurePigeon( From 387efc6ec9d987237379f1fcd78216d9360fc632 Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Thu, 14 May 2026 19:57:04 -0400 Subject: [PATCH 5/6] feat(app-check-windows): Route custom tokens through provider callback Replaces the public Pigeon setup flow for Windows custom App Check with a WindowsCustomProvider(fetchToken: ...) callback that owns token minting through the supported provider API. Registers a private FirebaseAppCheckFlutterApi implementation inside the method-channel delegate, converts the public CustomAppCheckToken value into the generated Pigeon token type internally, and removes FirebaseAppCheckFlutterApi from the public package exports. Updates Windows provider documentation and focused method-channel tests to cover the activate payload, successful callback token envelopes, and PlatformException propagation from fetchToken. --- .../lib/firebase_app_check.dart | 1 - .../lib/src/firebase_app_check.dart | 12 +-- ...firebase_app_check_platform_interface.dart | 2 - .../method_channel_firebase_app_check.dart | 34 ++++++- ...platform_interface_firebase_app_check.dart | 12 +-- .../lib/src/windows_providers.dart | 63 ++++++++----- ...ethod_channel_firebase_app_check_test.dart | 88 ++++++++++++------- 7 files changed, 140 insertions(+), 72 deletions(-) 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 53589fad47db..8827c2030970 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 @@ -25,7 +25,6 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor WindowsAppCheckProvider, WindowsDebugProvider, WindowsCustomProvider, - FirebaseAppCheckFlutterApi, CustomAppCheckToken; export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart' show FirebaseException; 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 8deabfc99208..ddcf676257fe 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 @@ -74,12 +74,12 @@ class FirebaseAppCheck extends FirebasePluginPlatform /// "app attest with fallback to device check" via `AppleAppCheckProvider`. /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. /// - /// **Windows**: Only the debug provider is supported. You **must** supply a - /// debug token — the desktop C++ SDK does not auto-generate one. Either pass - /// it via `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` - /// or set the `APP_CHECK_DEBUG_TOKEN` environment variable. The token must - /// first be registered in the Firebase Console under - /// *App Check → Apps → Manage debug tokens*. + /// **Windows**: Use `providerWindows` to configure either + /// [WindowsCustomProvider] for production token minting or + /// [WindowsDebugProvider] for development. The desktop C++ SDK does not + /// auto-generate debug tokens. Either pass a registered token via + /// `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` or set + /// the `APP_CHECK_DEBUG_TOKEN` environment variable. /// /// ## Migration Notice /// 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 b376615a7eb9..53a285e48729 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 @@ -11,5 +11,3 @@ 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'; export 'src/windows_providers.dart'; -export 'src/pigeon/messages.pigeon.dart' - show FirebaseAppCheckFlutterApi, CustomAppCheckToken; 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 50ddb8eab974..f88e284e40dd 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 @@ -10,14 +10,32 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import '../../firebase_app_check_platform_interface.dart'; -import '../pigeon/messages.pigeon.dart'; +import '../pigeon/messages.pigeon.dart' as pigeon; import 'utils/exception.dart'; import 'utils/provider_to_string.dart'; +class _WindowsCustomProviderFlutterApi + extends pigeon.FirebaseAppCheckFlutterApi { + @override + Future getCustomToken() async { + final provider = MethodChannelFirebaseAppCheck._windowsCustomProvider; + if (provider == null) { + throw StateError('No WindowsCustomProvider has been activated.'); + } + + final token = await provider.fetchToken(); + return pigeon.CustomAppCheckToken( + token: token.token, + expireTimeMillis: token.expireTimeMillis, + ); + } +} + class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { /// Create an instance of [MethodChannelFirebaseAppCheck]. MethodChannelFirebaseAppCheck({required FirebaseApp app}) : super(appInstance: app) { + pigeon.FirebaseAppCheckFlutterApi.setUp(_windowsCustomProviderFlutterApi); _tokenChangesListeners[app.name] = StreamController.broadcast(); _listenerRegistration = _registerTokenListener(app); } @@ -55,7 +73,11 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { {}; /// The Pigeon API used for platform communication. - final FirebaseAppCheckHostApi _pigeonApi = FirebaseAppCheckHostApi(); + final pigeon.FirebaseAppCheckHostApi _pigeonApi = + pigeon.FirebaseAppCheckHostApi(); + static final _windowsCustomProviderFlutterApi = + _WindowsCustomProviderFlutterApi(); + static WindowsCustomProvider? _windowsCustomProvider; late final Future _listenerRegistration; StreamSubscription? _subscription; bool _isDisposed = false; @@ -112,6 +134,7 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { WindowsAppCheckProvider? providerWindows, }) async { try { + _setWindowsCustomProvider(providerWindows); await _pigeonApi.activate( app.name, defaultTargetPlatform == TargetPlatform.android || kDebugMode @@ -140,6 +163,13 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { } } + static void _setWindowsCustomProvider( + WindowsAppCheckProvider? providerWindows, + ) { + _windowsCustomProvider = + providerWindows is WindowsCustomProvider ? providerWindows : null; + } + @override Future getToken(bool forceRefresh) async { try { 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 3346544a3bed..08789b066fce 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 @@ -67,12 +67,12 @@ abstract class FirebaseAppCheckPlatform extends PlatformInterface { /// "app attest with fallback to device check" via `AppleAppCheckProvider`. /// Note: App Attest is only available on iOS 14.0+ and macOS 14.0+. /// - /// **Windows**: Only the debug provider is supported. You **must** supply a - /// debug token — the desktop C++ SDK does not auto-generate one. Either pass - /// it via `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` - /// or set the `APP_CHECK_DEBUG_TOKEN` environment variable. The token must - /// first be registered in the Firebase Console under - /// *App Check → Apps → Manage debug tokens*. + /// **Windows**: Use `providerWindows` to configure either + /// [WindowsCustomProvider] for production token minting or + /// [WindowsDebugProvider] for development. The desktop C++ SDK does not + /// auto-generate debug tokens. Either pass a registered token via + /// `providerWindows: WindowsDebugProvider(debugToken: 'your-token')` or set + /// the `APP_CHECK_DEBUG_TOKEN` environment variable. /// /// ## Migration Notice /// diff --git a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart index be6f58d980f9..903cc5e2c5e1 100644 --- a/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart +++ b/packages/firebase_app_check/firebase_app_check_platform_interface/lib/src/windows_providers.dart @@ -13,36 +13,55 @@ abstract class WindowsAppCheckProvider { const WindowsAppCheckProvider(this.type); } +/// Carries a minted App Check token and its expiry. +class CustomAppCheckToken { + /// Creates a custom App Check token result. + const CustomAppCheckToken({ + required this.token, + required this.expireTimeMillis, + }); + + /// The App Check token string to send with Firebase requests. + final String token; + + /// Absolute expiry as Unix epoch milliseconds (UTC). + final int expireTimeMillis; +} + /// Custom provider for Windows production builds. /// /// When activated, the Windows C++ plugin registers a custom -/// `AppCheckProvider` that calls into Dart via a Pigeon `FlutterApi` each time -/// the Firebase SDK needs a fresh App Check token. The Dart handler is -/// expected to call a backend service (typically a Cloud Function with -/// `enforceAppCheck: false`) that mints a valid App Check token using the -/// Firebase Admin SDK, then return both the token and its expiry. +/// `AppCheckProvider` that calls [fetchToken] each time the Firebase SDK needs +/// a fresh App Check token. The callback is expected to call a backend service +/// (typically a Cloud Function with `enforceAppCheck: false`) that mints a +/// valid App Check token using the Firebase Admin SDK, then return both the +/// token and its expiry. /// -/// Register the Dart token handler before any Firebase operations that require -/// App Check, alongside `FirebaseAppCheck.instance.activate`: +/// Register the callback before any Firebase operations that require App Check: /// /// ```dart -/// FirebaseAppCheckFlutterApi.setUp(MyWindowsTokenHandler()); -/// -/// class MyWindowsTokenHandler implements FirebaseAppCheckFlutterApi { -/// @override -/// Future getCustomToken() async { -/// // Call your backend, e.g. a callable Cloud Function that uses -/// // admin.appCheck().createToken(windowsAppId). -/// final response = await myBackend.mintAppCheckToken(); -/// return CustomAppCheckToken( -/// token: response.token, -/// expireTimeMillis: response.expireTimeMillis, -/// ); -/// } -/// } +/// await FirebaseAppCheck.instance.activate( +/// providerWindows: WindowsCustomProvider( +/// fetchToken: () async { +/// // Call your backend, e.g. a callable Cloud Function that uses +/// // admin.appCheck().createToken(windowsAppId). +/// final response = await myBackend.mintAppCheckToken(); +/// return CustomAppCheckToken( +/// token: response.token, +/// expireTimeMillis: response.expireTimeMillis, +/// ); +/// }, +/// ), +/// ); /// ``` class WindowsCustomProvider extends WindowsAppCheckProvider { - const WindowsCustomProvider() : super('custom'); + /// Creates a Windows custom provider. + const WindowsCustomProvider({ + required this.fetchToken, + }) : super('custom'); + + /// Callback invoked when the native Firebase SDK needs a fresh token. + final Future Function() fetchToken; } /// Debug provider for Windows. 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 784b8f45b4af..d9b671a27769 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 @@ -3,7 +3,8 @@ // found in the LICENSE file. import 'package:firebase_app_check_platform_interface/firebase_app_check_platform_interface.dart'; -import 'package:firebase_app_check_platform_interface/src/pigeon/messages.pigeon.dart'; +import 'package:firebase_app_check_platform_interface/src/pigeon/messages.pigeon.dart' + as pigeon; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -69,10 +70,11 @@ void main() { activateChannelName, (ByteData? message) async { calls.add( - FirebaseAppCheckHostApi.pigeonChannelCodec.decodeMessage(message)! - as List, + pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec + .decodeMessage(message)! as List, ); - return FirebaseAppCheckHostApi.pigeonChannelCodec.encodeMessage( + return pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec + .encodeMessage( [], ); }, @@ -101,10 +103,11 @@ void main() { activateChannelName, (ByteData? message) async { calls.add( - FirebaseAppCheckHostApi.pigeonChannelCodec.decodeMessage(message)! - as List, + pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec + .decodeMessage(message)! as List, ); - return FirebaseAppCheckHostApi.pigeonChannelCodec.encodeMessage( + return pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec + .encodeMessage( [], ); }, @@ -133,7 +136,7 @@ void main() { debugDefaultTargetPlatformOverride = TargetPlatform.windows; activateChannel = const BasicMessageChannel( activateChannelName, - FirebaseAppCheckHostApi.pigeonChannelCodec, + pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec, ); activateMessages = []; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger @@ -153,7 +156,12 @@ void main() { final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); await appCheck.activate( - providerWindows: const WindowsCustomProvider(), + providerWindows: WindowsCustomProvider( + fetchToken: () async => const CustomAppCheckToken( + token: 'app-check-token', + expireTimeMillis: 1735689600000, + ), + ), ); // Android/Apple slots carry method-channel defaults even on Windows. @@ -211,25 +219,46 @@ void main() { }); }); - group('$FirebaseAppCheckFlutterApi', () { + group('Windows custom token callback', () { const BasicMessageChannel flutterApiChannel = BasicMessageChannel( 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken', - FirebaseAppCheckFlutterApi.pigeonChannelCodec, + pigeon.FirebaseAppCheckFlutterApi.pigeonChannelCodec, ); + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + activateChannelName, + (ByteData? message) async { + return pigeon.FirebaseAppCheckHostApi.pigeonChannelCodec + .encodeMessage( + [], + ); + }, + ); + }); + tearDown(() { - FirebaseAppCheckFlutterApi.setUp(null); + pigeon.FirebaseAppCheckFlutterApi.setUp(null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMessageHandler( + activateChannelName, + null, + ); }); - test('returns CustomAppCheckToken in a success envelope', () async { - final token = CustomAppCheckToken( + test('returns tokens from the active WindowsCustomProvider fetchToken', + () async { + const token = CustomAppCheckToken( token: 'app-check-token', expireTimeMillis: 1735689600000, ); - FirebaseAppCheckFlutterApi.setUp( - _TestFirebaseAppCheckFlutterApi( - onGetCustomToken: () async => token, + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + await appCheck.activate( + providerWindows: WindowsCustomProvider( + fetchToken: () async => token, ), ); @@ -243,14 +272,18 @@ void main() { final reply = flutterApiChannel.codec.decodeMessage(replyData) as List?; - expect(reply, [token]); + final customToken = reply!.single! as pigeon.CustomAppCheckToken; + expect(customToken.token, 'app-check-token'); + expect(customToken.expireTimeMillis, 1735689600000); }); - test('returns a PlatformException envelope when the handler throws', + test('returns a PlatformException envelope when fetchToken throws', () async { - FirebaseAppCheckFlutterApi.setUp( - _TestFirebaseAppCheckFlutterApi( - onGetCustomToken: () async { + final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); + + await appCheck.activate( + providerWindows: WindowsCustomProvider( + fetchToken: () async { throw PlatformException( code: 'token-error', message: 'Failed to mint App Check token', @@ -278,14 +311,3 @@ void main() { }); }); } - -class _TestFirebaseAppCheckFlutterApi implements FirebaseAppCheckFlutterApi { - _TestFirebaseAppCheckFlutterApi({ - required this.onGetCustomToken, - }); - - final Future Function() onGetCustomToken; - - @override - Future getCustomToken() => onGetCustomToken(); -} From df58abeef854f2b88f402bfa02bfac5d00780432 Mon Sep 17 00:00:00 2001 From: Luke Memet Date: Tue, 23 Jun 2026 16:57:51 -0400 Subject: [PATCH 6/6] fix(app_check,windows): Scope custom providers by app Store Windows custom providers per Firebase app name and register the FlutterApi callback with a matching app-specific Pigeon suffix. Keep a native provider per firebase::App on Windows so multi-app token requests call the matching Dart provider. Restore the native GetLimitedUseAppCheckToken call now that the rebased Windows SDK exposes it, and add coverage for separate default and secondary app callbacks. --- .../appcheck/FirebaseAppCheckPlugin.kt | 12 +- .../GeneratedAndroidFirebaseAppCheck.g.kt | 216 +++++--- .../windows/firebase_app_check_plugin.cpp | 22 +- .../windows/firebase_app_check_plugin.h | 5 +- .../firebase_app_check/windows/messages.g.cpp | 512 ++++++++++-------- .../firebase_app_check/windows/messages.g.h | 102 ++-- .../method_channel_firebase_app_check.dart | 42 +- ...ethod_channel_firebase_app_check_test.dart | 79 ++- 8 files changed, 607 insertions(+), 383 deletions(-) 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 4bc6eccdef82..a4d7031c3fd0 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 @@ -49,12 +49,12 @@ class FirebaseAppCheckPlugin : FlutterFirebasePlugin, FlutterPlugin, FirebaseApp } override fun activate( - appName: String, - androidProvider: String?, - appleProvider: String?, - debugToken: String?, - windowsProvider: String?, - callback: (Result) -> Unit + appName: String, + androidProvider: String?, + appleProvider: String?, + debugToken: String?, + windowsProvider: String?, + callback: (Result) -> Unit ) { try { val firebaseAppCheck = getAppCheck(appName) 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 d01d5c788d9d..b76562cc227b 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 @@ -10,16 +10,17 @@ package io.flutter.plugins.firebase.appcheck import android.util.Log import io.flutter.plugin.common.BasicMessageChannel import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec import io.flutter.plugin.common.StandardMessageCodec import java.io.ByteArrayOutputStream import java.nio.ByteBuffer + private object GeneratedAndroidFirebaseAppCheckPigeonUtils { fun createConnectionError(channelName: String): FlutterError { - return FlutterError("channel-error", "Unable to establish connection on channel: '$channelName'.", "") } + return FlutterError( + "channel-error", "Unable to establish connection on channel: '$channelName'.", "") + } fun wrapResult(result: Any?): List { return listOf(result) @@ -27,19 +28,15 @@ private object GeneratedAndroidFirebaseAppCheckPigeonUtils { fun wrapError(exception: Throwable): List { return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) + listOf(exception.code, exception.message, exception.details) } else { listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)) } } + fun doubleEquals(a: Double, b: Double): Boolean { // Normalize -0.0 to 0.0 and handle NaN equality. return (if (a == 0.0) 0.0 else a) == (if (b == 0.0) 0.0 else b) || (a.isNaN() && b.isNaN()) @@ -183,41 +180,38 @@ private object GeneratedAndroidFirebaseAppCheckPigeonUtils { else -> value.hashCode() } } - } /** * Error class for passing custom error details to Flutter via a thrown PlatformException. + * * @property code The error code. * @property message The error message. * @property details The error details. Must be a datatype supported by the api codec. */ -class FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null +class FlutterError( + val code: String, + override val message: String? = null, + val details: Any? = null ) : RuntimeException() /** - * Carries a minted App Check token plus the wall-clock expiry the Firebase - * SDK should associate with it. Returning the expiry alongside the token lets - * backends mint tokens with arbitrary lifetimes (short TTLs for a stricter - * security posture, longer TTLs for fewer round-trips) without the plugin - * hardcoding a refresh window. + * Carries a minted App Check token plus the wall-clock expiry the Firebase SDK should associate + * with it. Returning the expiry alongside the token lets backends mint tokens with arbitrary + * lifetimes (short TTLs for a stricter security posture, longer TTLs for fewer round-trips) without + * the plugin hardcoding a refresh window. * * Generated class from Pigeon that represents data sent in messages. */ -data class CustomAppCheckToken ( - /** The App Check token string to send with Firebase requests. */ - val token: String, - /** - * Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses - * this to decide when to refresh; a token returned with an expiry in the - * past is treated as immediately expired. - */ - val expireTimeMillis: Long -) - { +data class CustomAppCheckToken( + /** The App Check token string to send with Firebase requests. */ + val token: String, + /** + * Absolute expiry as Unix epoch milliseconds (UTC). The Firebase SDK uses this to decide when + * to refresh; a token returned with an expiry in the past is treated as immediately expired. + */ + val expireTimeMillis: Long +) { companion object { fun fromList(pigeonVar_list: List): CustomAppCheckToken { val token = pigeonVar_list[0] as String @@ -225,12 +219,14 @@ data class CustomAppCheckToken ( return CustomAppCheckToken(token, expireTimeMillis) } } + fun toList(): List { return listOf( - token, - expireTimeMillis, + token, + expireTimeMillis, ) } + override fun equals(other: Any?): Boolean { if (other == null || other.javaClass != javaClass) { return false @@ -239,28 +235,31 @@ data class CustomAppCheckToken ( return true } val other = other as CustomAppCheckToken - return GeneratedAndroidFirebaseAppCheckPigeonUtils.deepEquals(this.token, other.token) && GeneratedAndroidFirebaseAppCheckPigeonUtils.deepEquals(this.expireTimeMillis, other.expireTimeMillis) + return GeneratedAndroidFirebaseAppCheckPigeonUtils.deepEquals(this.token, other.token) && + GeneratedAndroidFirebaseAppCheckPigeonUtils.deepEquals( + this.expireTimeMillis, other.expireTimeMillis) } override fun hashCode(): Int { var result = javaClass.hashCode() result = 31 * result + GeneratedAndroidFirebaseAppCheckPigeonUtils.deepHash(this.token) - result = 31 * result + GeneratedAndroidFirebaseAppCheckPigeonUtils.deepHash(this.expireTimeMillis) + result = + 31 * result + GeneratedAndroidFirebaseAppCheckPigeonUtils.deepHash(this.expireTimeMillis) return result } } + private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessageCodec() { override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { return when (type) { 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - CustomAppCheckToken.fromList(it) - } + return (readValue(buffer) as? List)?.let { CustomAppCheckToken.fromList(it) } } else -> super.readValueOfType(type, buffer) } } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { when (value) { is CustomAppCheckToken -> { stream.write(129) @@ -271,26 +270,50 @@ 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?, windowsProvider: String?, callback: (Result) -> Unit) + fun activate( + appName: String, + androidProvider: String?, + appleProvider: String?, + debugToken: String?, + windowsProvider: String?, + callback: (Result) -> Unit + ) + fun getToken(appName: String, forceRefresh: Boolean, callback: (Result) -> Unit) - fun setTokenAutoRefreshEnabled(appName: String, isTokenAutoRefreshEnabled: Boolean, callback: (Result) -> Unit) + + fun setTokenAutoRefreshEnabled( + appName: String, + isTokenAutoRefreshEnabled: Boolean, + callback: (Result) -> Unit + ) + fun registerTokenListener(appName: String, callback: (Result) -> Unit) + fun getLimitedUseAppCheckToken(appName: String, callback: (Result) -> Unit) companion object { /** The codec used by FirebaseAppCheckHostApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseAppCheckPigeonCodec() - } - /** Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the `binaryMessenger`. */ + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseAppCheckPigeonCodec() } + /** + * Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the + * `binaryMessenger`. + */ @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: FirebaseAppCheckHostApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + fun setUp( + binaryMessenger: BinaryMessenger, + api: FirebaseAppCheckHostApi?, + messageChannelSuffix: String = "" + ) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -299,21 +322,30 @@ interface FirebaseAppCheckHostApi { val appleProviderArg = args[2] as String? val debugTokenArg = args[3] as String? val windowsProviderArg = args[4] as String? - api.activate(appNameArg, androidProviderArg, appleProviderArg, debugTokenArg, windowsProviderArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) - } else { - reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(null)) - } - } + api.activate( + appNameArg, + androidProviderArg, + appleProviderArg, + debugTokenArg, + windowsProviderArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) + } else { + reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapResult(null)) + } + } } } else { channel.setMessageHandler(null) } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -334,13 +366,18 @@ interface FirebaseAppCheckHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List val appNameArg = args[0] as String val isTokenAutoRefreshEnabledArg = args[1] as Boolean - api.setTokenAutoRefreshEnabled(appNameArg, isTokenAutoRefreshEnabledArg) { result: Result -> + api.setTokenAutoRefreshEnabled(appNameArg, isTokenAutoRefreshEnabledArg) { + result: Result -> val error = result.exceptionOrNull() if (error != null) { reply.reply(GeneratedAndroidFirebaseAppCheckPigeonUtils.wrapError(error)) @@ -354,7 +391,11 @@ interface FirebaseAppCheckHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -374,7 +415,11 @@ interface FirebaseAppCheckHostApi { } } run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$separatedMessageChannelSuffix", codec) + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken$separatedMessageChannelSuffix", + codec) if (api != null) { channel.setMessageHandler { message, reply -> val args = message as List @@ -397,40 +442,49 @@ interface FirebaseAppCheckHostApi { } } /** - * Dart-side handler invoked by the native plugin when the Firebase SDK needs - * a fresh App Check token. Implementations typically call a backend service - * (for example a Cloud Function with `enforceAppCheck: false`) that mints a - * token using the Firebase Admin SDK. The native side awaits the future, - * then hands the token to the Firebase SDK, which attaches it to subsequent - * Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). + * Dart-side handler invoked by the native plugin when the Firebase SDK needs a fresh App Check + * token. Implementations typically call a backend service (for example a Cloud Function with + * `enforceAppCheck: false`) that mints a token using the Firebase Admin SDK. The native side awaits + * the future, then hands the token to the Firebase SDK, which attaches it to subsequent Firebase + * backend requests (Firestore, Functions, Storage, Auth, RTDB). * * Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */ -class FirebaseAppCheckFlutterApi(private val binaryMessenger: BinaryMessenger, private val messageChannelSuffix: String = "") { +class FirebaseAppCheckFlutterApi( + private val binaryMessenger: BinaryMessenger, + private val messageChannelSuffix: String = "" +) { companion object { /** The codec used by FirebaseAppCheckFlutterApi. */ - val codec: MessageCodec by lazy { - GeneratedAndroidFirebaseAppCheckPigeonCodec() - } + val codec: MessageCodec by lazy { GeneratedAndroidFirebaseAppCheckPigeonCodec() } } - fun getCustomToken(callback: (Result) -> Unit) -{ - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" - val channelName = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$separatedMessageChannelSuffix" + + fun getCustomToken(callback: (Result) -> Unit) { + val separatedMessageChannelSuffix = + if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + val channelName = + "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken$separatedMessageChannelSuffix" val channel = BasicMessageChannel(binaryMessenger, channelName, codec) channel.send(null) { if (it is List<*>) { if (it.size > 1) { callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) } else if (it[0] == null) { - callback(Result.failure(FlutterError("null-error", "Flutter api returned null value for non-null return value.", ""))) + callback( + Result.failure( + FlutterError( + "null-error", + "Flutter api returned null value for non-null return value.", + ""))) } else { val output = it[0] as CustomAppCheckToken callback(Result.success(output)) } } else { - callback(Result.failure(GeneratedAndroidFirebaseAppCheckPigeonUtils.createConnectionError(channelName))) - } + callback( + Result.failure( + GeneratedAndroidFirebaseAppCheckPigeonUtils.createConnectionError(channelName))) + } } } } diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp index 789a1dc33d81..e4371030efe0 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.cpp @@ -104,9 +104,9 @@ class TokenStreamHandler // expiry, so the C++ SDK can cache for the exact lifetime the backend minted // rather than a hardcoded refresh window. FlutterCustomAppCheckProvider::FlutterCustomAppCheckProvider( - flutter::BinaryMessenger* binary_messenger) - : flutter_api_( - std::make_unique(binary_messenger)) {} + flutter::BinaryMessenger* binary_messenger, const std::string& app_name) + : flutter_api_(std::make_unique( + binary_messenger, app_name)) {} void FlutterCustomAppCheckProvider::GetToken( std::function(binary_messenger_); + const std::string app_name = app == nullptr ? "" : app->name(); + auto& provider = providers_[app_name]; + if (!provider) { + provider = std::make_unique( + binary_messenger_, app_name); } - return provider_.get(); + return provider.get(); } static AppCheck* GetAppCheckFromPigeon(const std::string& app_name) { @@ -280,12 +282,8 @@ void FirebaseAppCheckPlugin::RegisterTokenListener( void FirebaseAppCheckPlugin::GetLimitedUseAppCheckToken( const std::string& app_name, std::function reply)> result) { - // GetLimitedUseAppCheckToken was added to the Firebase C++ SDK after the - // version currently bundled with this plugin. Fall back to GetAppCheckToken, - // which is functionally equivalent for our custom Windows provider since it - // does not cache — it calls getWindowsAppCheckToken on every invocation. AppCheck* app_check = GetAppCheckFromPigeon(app_name); - Future future = app_check->GetAppCheckToken(false); + Future future = app_check->GetLimitedUseAppCheckToken(); future.OnCompletion([result](const Future& completed_future) { if (completed_future.error() != 0) { result(ParseError(completed_future)); diff --git a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h index c78375ffbcb4..7677a3527581 100644 --- a/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h +++ b/packages/firebase_app_check/firebase_app_check/windows/firebase_app_check_plugin.h @@ -33,7 +33,7 @@ class FlutterCustomAppCheckProvider : public firebase::app_check::AppCheckProvider { public: explicit FlutterCustomAppCheckProvider( - flutter::BinaryMessenger* binary_messenger); + flutter::BinaryMessenger* binary_messenger, const std::string& app_name); void GetToken(std::function completion_callback) override; @@ -53,7 +53,8 @@ class FlutterCustomAppCheckProviderFactory private: flutter::BinaryMessenger* binary_messenger_; - std::unique_ptr provider_; + std::map> + providers_; }; class FirebaseAppCheckPlugin : public flutter::Plugin, 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 c098e83ad5c8..9eea265bc7f7 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 @@ -34,32 +34,36 @@ FlutterError CreateConnectionError(const std::string channel_name) { } namespace { -template +template bool PigeonInternalDeepEquals(const T& a, const T& b); bool PigeonInternalDeepEquals(const double& a, const double& b); -template +template bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b); -template +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::optional& a, + const std::optional& b); -template -bool PigeonInternalDeepEquals(const std::unique_ptr& a, const std::unique_ptr& b); +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b); -bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, const ::flutter::EncodableValue& b); +bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, + const ::flutter::EncodableValue& b); -template +template bool PigeonInternalDeepEquals(const T& a, const T& b) { return a == b; } -template -bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b) { +template +bool PigeonInternalDeepEquals(const std::vector& a, + const std::vector& b) { if (a.size() != b.size()) { return false; } @@ -72,7 +76,8 @@ bool PigeonInternalDeepEquals(const std::vector& a, const std::vector& b) } template -bool PigeonInternalDeepEquals(const std::map& a, const std::map& b) { +bool PigeonInternalDeepEquals(const std::map& a, + const std::map& b) { if (a.size() != b.size()) { return false; } @@ -100,8 +105,9 @@ bool PigeonInternalDeepEquals(const double& a, const double& b) { return (a == b) || (std::isnan(a) && std::isnan(b)); } -template -bool PigeonInternalDeepEquals(const std::optional& a, const std::optional& b) { +template +bool PigeonInternalDeepEquals(const std::optional& a, + const std::optional& b) { if (!a && !b) { return true; } @@ -111,8 +117,9 @@ bool PigeonInternalDeepEquals(const std::optional& a, const std::optional& return PigeonInternalDeepEquals(*a, *b); } -template -bool PigeonInternalDeepEquals(const std::unique_ptr& a, const std::unique_ptr& b) { +template +bool PigeonInternalDeepEquals(const std::unique_ptr& a, + const std::unique_ptr& b) { if (a.get() == b.get()) { return true; } @@ -122,15 +129,18 @@ bool PigeonInternalDeepEquals(const std::unique_ptr& a, const std::unique_ptr return PigeonInternalDeepEquals(*a, *b); } -bool PigeonInternalDeepEquals(const ::flutter::EncodableValue& a, const ::flutter::EncodableValue& 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)) { + } 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)) { + } else if (const ::flutter::EncodableMap* ma = + std::get_if<::flutter::EncodableMap>(&a)) { return PigeonInternalDeepEquals(*ma, std::get<::flutter::EncodableMap>(b)); } return a == b; @@ -173,7 +183,8 @@ 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)); + result += ((PigeonInternalDeepHash(kv.first) * 31) ^ + PigeonInternalDeepHash(kv.second)); } return result; } @@ -230,21 +241,16 @@ size_t PigeonInternalDeepHash(const ::flutter::EncodableValue& v) { } // namespace // CustomAppCheckToken -CustomAppCheckToken::CustomAppCheckToken( - const std::string& token, - int64_t expire_time_millis) - : token_(token), - expire_time_millis_(expire_time_millis) {} +CustomAppCheckToken::CustomAppCheckToken(const std::string& token, + int64_t expire_time_millis) + : token_(token), expire_time_millis_(expire_time_millis) {} -const std::string& CustomAppCheckToken::token() const { - return token_; -} +const std::string& CustomAppCheckToken::token() const { return token_; } void CustomAppCheckToken::set_token(std::string_view value_arg) { token_ = value_arg; } - int64_t CustomAppCheckToken::expire_time_millis() const { return expire_time_millis_; } @@ -253,7 +259,6 @@ void CustomAppCheckToken::set_expire_time_millis(int64_t value_arg) { expire_time_millis_ = value_arg; } - EncodableList CustomAppCheckToken::ToEncodableList() const { EncodableList list; list.reserve(2); @@ -262,15 +267,17 @@ EncodableList CustomAppCheckToken::ToEncodableList() const { return list; } -CustomAppCheckToken CustomAppCheckToken::FromEncodableList(const EncodableList& list) { - CustomAppCheckToken decoded( - std::get(list[0]), - std::get(list[1])); +CustomAppCheckToken CustomAppCheckToken::FromEncodableList( + const EncodableList& list) { + CustomAppCheckToken decoded(std::get(list[0]), + std::get(list[1])); return decoded; } bool CustomAppCheckToken::operator==(const CustomAppCheckToken& other) const { - return PigeonInternalDeepEquals(token_, other.token_) && PigeonInternalDeepEquals(expire_time_millis_, other.expire_time_millis_); + return PigeonInternalDeepEquals(token_, other.token_) && + PigeonInternalDeepEquals(expire_time_millis_, + other.expire_time_millis_); } bool CustomAppCheckToken::operator!=(const CustomAppCheckToken& other) const { @@ -284,32 +291,32 @@ size_t CustomAppCheckToken::Hash() const { return result; } -size_t PigeonInternalDeepHash(const CustomAppCheckToken& v) { - return v.Hash(); -} - +size_t PigeonInternalDeepHash(const CustomAppCheckToken& v) { return v.Hash(); } PigeonInternalCodecSerializer::PigeonInternalCodecSerializer() {} EncodableValue PigeonInternalCodecSerializer::ReadValueOfType( - uint8_t type, - ::flutter::ByteStreamReader* stream) const { + uint8_t type, ::flutter::ByteStreamReader* stream) const { switch (type) { case 129: { - return CustomEncodableValue(CustomAppCheckToken::FromEncodableList(std::get(ReadValue(stream)))); - } + return CustomEncodableValue(CustomAppCheckToken::FromEncodableList( + std::get(ReadValue(stream)))); + } default: return ::flutter::StandardCodecSerializer::ReadValueOfType(type, stream); - } + } } void PigeonInternalCodecSerializer::WriteValue( - const EncodableValue& value, - ::flutter::ByteStreamWriter* stream) const { - if (const CustomEncodableValue* custom_value = std::get_if(&value)) { + const EncodableValue& value, ::flutter::ByteStreamWriter* stream) const { + if (const CustomEncodableValue* custom_value = + std::get_if(&value)) { if (custom_value->type() == typeid(CustomAppCheckToken)) { stream->WriteByte(129); - WriteValue(EncodableValue(std::any_cast(*custom_value).ToEncodableList()), stream); + WriteValue( + EncodableValue(std::any_cast(*custom_value) + .ToEncodableList()), + stream); return; } } @@ -318,239 +325,316 @@ void PigeonInternalCodecSerializer::WriteValue( /// The codec used by FirebaseAppCheckHostApi. const ::flutter::StandardMessageCodec& FirebaseAppCheckHostApi::GetCodec() { - return ::flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } -// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through the `binary_messenger`. +// Sets up an instance of `FirebaseAppCheckHostApi` to handle messages through +// the `binary_messenger`. void FirebaseAppCheckHostApi::SetUp( - ::flutter::BinaryMessenger* binary_messenger, - FirebaseAppCheckHostApi* api) { + ::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 : ""; + ::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); - const auto& encodable_windows_provider_arg = args.at(4); - const auto* windows_provider_arg = std::get_if(&encodable_windows_provider_arg); - api->Activate(app_name_arg, android_provider_arg, apple_provider_arg, debug_token_arg, windows_provider_arg, [reply](std::optional&& output) { - if (output.has_value()) { - reply(WrapError(output.value())); - return; + 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_windows_provider_arg = args.at(4); + const auto* windows_provider_arg = + std::get_if(&encodable_windows_provider_arg); + api->Activate(app_name_arg, android_provider_arg, + apple_provider_arg, debug_token_arg, + windows_provider_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())); } - 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; + 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())); } - 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; + 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())); } - 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; + 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())); } - 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; + 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())); } - 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()}); } -// Generated class from Pigeon that represents Flutter messages that can be called from C++. -FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi(::flutter::BinaryMessenger* binary_messenger) - : binary_messenger_(binary_messenger), - message_channel_suffix_("") {} +// Generated class from Pigeon that represents Flutter messages that can be +// called from C++. +FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi( + ::flutter::BinaryMessenger* binary_messenger) + : binary_messenger_(binary_messenger), message_channel_suffix_("") {} FirebaseAppCheckFlutterApi::FirebaseAppCheckFlutterApi( - ::flutter::BinaryMessenger* binary_messenger, - const std::string& message_channel_suffix) - : binary_messenger_(binary_messenger), - message_channel_suffix_(message_channel_suffix.length() > 0 ? std::string(".") + message_channel_suffix : "") {} + ::flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix) + : binary_messenger_(binary_messenger), + message_channel_suffix_(message_channel_suffix.length() > 0 + ? std::string(".") + message_channel_suffix + : "") {} const ::flutter::StandardMessageCodec& FirebaseAppCheckFlutterApi::GetCodec() { - return ::flutter::StandardMessageCodec::GetInstance(&PigeonInternalCodecSerializer::GetInstance()); + return ::flutter::StandardMessageCodec::GetInstance( + &PigeonInternalCodecSerializer::GetInstance()); } void FirebaseAppCheckFlutterApi::GetCustomToken( - std::function&& on_success, - std::function&& on_error) { - const std::string channel_name = "dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken" + message_channel_suffix_; + std::function&& on_success, + std::function&& on_error) { + const std::string channel_name = + "dev.flutter.pigeon.firebase_app_check_platform_interface." + "FirebaseAppCheckFlutterApi.getCustomToken" + + message_channel_suffix_; BasicMessageChannel<> channel(binary_messenger_, channel_name, &GetCodec()); EncodableValue encoded_api_arguments = EncodableValue(); - channel.Send(encoded_api_arguments, [channel_name, on_success = std::move(on_success), on_error = std::move(on_error)](const uint8_t* reply, size_t reply_size) { - std::unique_ptr response = GetCodec().DecodeMessage(reply, reply_size); + channel.Send(encoded_api_arguments, [channel_name, + on_success = std::move(on_success), + on_error = std::move(on_error)]( + const uint8_t* reply, + size_t reply_size) { + std::unique_ptr response = + GetCodec().DecodeMessage(reply, reply_size); const auto& encodable_return_value = *response; - const auto* list_return_value = std::get_if(&encodable_return_value); + const auto* list_return_value = + std::get_if(&encodable_return_value); if (list_return_value) { if (list_return_value->size() > 1) { - on_error(FlutterError(std::get(list_return_value->at(0)), std::get(list_return_value->at(1)), list_return_value->at(2))); + on_error(FlutterError(std::get(list_return_value->at(0)), + std::get(list_return_value->at(1)), + list_return_value->at(2))); } else { - const auto& return_value = std::any_cast(std::get(list_return_value->at(0))); + const auto& return_value = std::any_cast( + std::get(list_return_value->at(0))); on_success(return_value); } } else { 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 a2484dbffb5f..450a16da3663 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 @@ -17,17 +17,16 @@ 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_; } @@ -39,7 +38,8 @@ class FlutterError { ::flutter::EncodableValue details_; }; -template class ErrorOr { +template +class ErrorOr { public: ErrorOr(const T& rhs) : v_(rhs) {} ErrorOr(const T&& rhs) : v_(std::move(rhs)) {} @@ -59,8 +59,6 @@ template class ErrorOr { std::variant v_; }; - - // Carries a minted App Check token plus the wall-clock expiry the Firebase // SDK should associate with it. Returning the expiry alongside the token lets // backends mint tokens with arbitrary lifetimes (short TTLs for a stricter @@ -71,9 +69,8 @@ template class ErrorOr { class CustomAppCheckToken { public: // Constructs an object setting all fields. - explicit CustomAppCheckToken( - const std::string& token, - int64_t expire_time_millis); + explicit CustomAppCheckToken(const std::string& token, + int64_t expire_time_millis); // The App Check token string to send with Firebase requests. const std::string& token() const; @@ -87,10 +84,13 @@ class CustomAppCheckToken { bool operator==(const CustomAppCheckToken& other) const; bool operator!=(const CustomAppCheckToken& other) const; - /// Returns a hash code value for the object. This method is supported for the benefit of hash tables. + /// Returns a hash code value for the object. This method is supported for the + /// benefit of hash tables. size_t Hash() const; + private: - static CustomAppCheckToken FromEncodableList(const ::flutter::EncodableList& list); + static CustomAppCheckToken FromEncodableList( + const ::flutter::EncodableList& list); ::flutter::EncodableList ToEncodableList() const; friend class FirebaseAppCheckHostApi; friend class FirebaseAppCheckFlutterApi; @@ -99,8 +99,8 @@ class CustomAppCheckToken { int64_t expire_time_millis_; }; - -class PigeonInternalCodecSerializer : public ::flutter::StandardCodecSerializer { +class PigeonInternalCodecSerializer + : public ::flutter::StandardCodecSerializer { public: PigeonInternalCodecSerializer(); inline static PigeonInternalCodecSerializer& GetInstance() { @@ -108,55 +108,52 @@ 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; + 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, - const std::string* windows_provider, - 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* windows_provider, + 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); + // 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; }; @@ -167,17 +164,18 @@ class FirebaseAppCheckHostApi { // then hands the token to the Firebase SDK, which attaches it to subsequent // Firebase backend requests (Firestore, Functions, Storage, Auth, RTDB). // -// Generated class from Pigeon that represents Flutter messages that can be called from C++. +// Generated class from Pigeon that represents Flutter messages that can be +// called from C++. class FirebaseAppCheckFlutterApi { public: FirebaseAppCheckFlutterApi(::flutter::BinaryMessenger* binary_messenger); - FirebaseAppCheckFlutterApi( - ::flutter::BinaryMessenger* binary_messenger, - const std::string& message_channel_suffix); + FirebaseAppCheckFlutterApi(::flutter::BinaryMessenger* binary_messenger, + const std::string& message_channel_suffix); static const ::flutter::StandardMessageCodec& GetCodec(); void GetCustomToken( - std::function&& on_success, - std::function&& on_error); + std::function&& on_success, + std::function&& on_error); + private: ::flutter::BinaryMessenger* binary_messenger_; std::string message_channel_suffix_; 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 f88e284e40dd..f3c796a477bf 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 @@ -16,11 +16,18 @@ import 'utils/provider_to_string.dart'; class _WindowsCustomProviderFlutterApi extends pigeon.FirebaseAppCheckFlutterApi { + _WindowsCustomProviderFlutterApi(this.appName); + + final String appName; + @override Future getCustomToken() async { - final provider = MethodChannelFirebaseAppCheck._windowsCustomProvider; + final provider = + MethodChannelFirebaseAppCheck._windowsCustomProviders[appName]; if (provider == null) { - throw StateError('No WindowsCustomProvider has been activated.'); + throw StateError( + 'No WindowsCustomProvider has been activated for app $appName.', + ); } final token = await provider.fetchToken(); @@ -35,7 +42,14 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { /// Create an instance of [MethodChannelFirebaseAppCheck]. MethodChannelFirebaseAppCheck({required FirebaseApp app}) : super(appInstance: app) { - pigeon.FirebaseAppCheckFlutterApi.setUp(_windowsCustomProviderFlutterApi); + final flutterApi = _windowsCustomProviderFlutterApis.putIfAbsent( + app.name, + () => _WindowsCustomProviderFlutterApi(app.name), + ); + pigeon.FirebaseAppCheckFlutterApi.setUp( + flutterApi, + messageChannelSuffix: app.name, + ); _tokenChangesListeners[app.name] = StreamController.broadcast(); _listenerRegistration = _registerTokenListener(app); } @@ -75,9 +89,9 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { /// The Pigeon API used for platform communication. final pigeon.FirebaseAppCheckHostApi _pigeonApi = pigeon.FirebaseAppCheckHostApi(); - static final _windowsCustomProviderFlutterApi = - _WindowsCustomProviderFlutterApi(); - static WindowsCustomProvider? _windowsCustomProvider; + static final Map + _windowsCustomProviderFlutterApis = {}; + static final Map _windowsCustomProviders = {}; late final Future _listenerRegistration; StreamSubscription? _subscription; bool _isDisposed = false; @@ -108,6 +122,13 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { await _subscription?.cancel(); _subscription = null; await _tokenChangesListeners.remove(app.name)?.close(); + _windowsCustomProviders.remove(app.name); + if (_windowsCustomProviderFlutterApis.remove(app.name) != null) { + pigeon.FirebaseAppCheckFlutterApi.setUp( + null, + messageChannelSuffix: app.name, + ); + } _methodChannelFirebaseAppCheckInstances.remove(app.name); } @@ -163,11 +184,14 @@ class MethodChannelFirebaseAppCheck extends FirebaseAppCheckPlatform { } } - static void _setWindowsCustomProvider( + void _setWindowsCustomProvider( WindowsAppCheckProvider? providerWindows, ) { - _windowsCustomProvider = - providerWindows is WindowsCustomProvider ? providerWindows : null; + if (providerWindows is WindowsCustomProvider) { + _windowsCustomProviders[app.name] = providerWindows; + } else { + _windowsCustomProviders.remove(app.name); + } } @override 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 9b6b7767a7d6..25cd1c683fac 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 @@ -9,7 +9,6 @@ import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/services.dart'; import '../mock.dart'; @@ -221,11 +220,12 @@ void main() { }); group('Windows custom token callback', () { - const BasicMessageChannel flutterApiChannel = - BasicMessageChannel( - 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken', - pigeon.FirebaseAppCheckFlutterApi.pigeonChannelCodec, - ); + BasicMessageChannel flutterApiChannelFor(String appName) { + return BasicMessageChannel( + 'dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckFlutterApi.getCustomToken.$appName', + pigeon.FirebaseAppCheckFlutterApi.pigeonChannelCodec, + ); + } setUp(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger @@ -241,7 +241,14 @@ void main() { }); tearDown(() { - pigeon.FirebaseAppCheckFlutterApi.setUp(null); + pigeon.FirebaseAppCheckFlutterApi.setUp( + null, + messageChannelSuffix: Firebase.app().name, + ); + pigeon.FirebaseAppCheckFlutterApi.setUp( + null, + messageChannelSuffix: secondaryApp.name, + ); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMessageHandler( activateChannelName, @@ -263,6 +270,7 @@ void main() { ), ); + final flutterApiChannel = flutterApiChannelFor(secondaryApp.name); final replyData = await TestDefaultBinaryMessengerBinding .instance.defaultBinaryMessenger .handlePlatformMessage( @@ -278,6 +286,62 @@ void main() { expect(customToken.expireTimeMillis, 1735689600000); }); + test('uses the provider registered for the requested app', () async { + final defaultAppCheck = + MethodChannelFirebaseAppCheck(app: Firebase.app()); + final secondaryAppCheck = + MethodChannelFirebaseAppCheck(app: secondaryApp); + + await defaultAppCheck.activate( + providerWindows: WindowsCustomProvider( + fetchToken: () async => const CustomAppCheckToken( + token: 'default-app-token', + expireTimeMillis: 1735689600000, + ), + ), + ); + await secondaryAppCheck.activate( + providerWindows: WindowsCustomProvider( + fetchToken: () async => const CustomAppCheckToken( + token: 'secondary-app-token', + expireTimeMillis: 1735689700000, + ), + ), + ); + + final defaultChannel = flutterApiChannelFor(Firebase.app().name); + final secondaryChannel = flutterApiChannelFor(secondaryApp.name); + + final defaultReplyData = await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger + .handlePlatformMessage( + defaultChannel.name, + defaultChannel.codec.encodeMessage(null), + null, + ); + final secondaryReplyData = await TestDefaultBinaryMessengerBinding + .instance.defaultBinaryMessenger + .handlePlatformMessage( + secondaryChannel.name, + secondaryChannel.codec.encodeMessage(null), + null, + ); + + final defaultReply = defaultChannel.codec.decodeMessage(defaultReplyData) + as List?; + final secondaryReply = secondaryChannel.codec + .decodeMessage(secondaryReplyData) as List?; + + final defaultToken = defaultReply!.single! as pigeon.CustomAppCheckToken; + final secondaryToken = + secondaryReply!.single! as pigeon.CustomAppCheckToken; + + expect(defaultToken.token, 'default-app-token'); + expect(defaultToken.expireTimeMillis, 1735689600000); + expect(secondaryToken.token, 'secondary-app-token'); + expect(secondaryToken.expireTimeMillis, 1735689700000); + }); + test('returns a PlatformException envelope when fetchToken throws', () async { final appCheck = MethodChannelFirebaseAppCheck(app: secondaryApp); @@ -294,6 +358,7 @@ void main() { ), ); + final flutterApiChannel = flutterApiChannelFor(secondaryApp.name); final replyData = await TestDefaultBinaryMessengerBinding .instance.defaultBinaryMessenger .handlePlatformMessage(