Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
359fec8
first commit for appcheck recaptcha support
cynthiajoan May 7, 2026
6c44526
update example
cynthiajoan May 7, 2026
2e59190
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan May 21, 2026
e9a03b7
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan May 21, 2026
94e0131
apple tentative implementation
cynthiajoan May 22, 2026
8842bb4
add recaptcha in the plugin swift
cynthiajoan May 27, 2026
57db3e5
minor fix
cynthiajoan May 27, 2026
5a8f598
some dart fix
cynthiajoan May 27, 2026
a2acf79
updated sample app UI
cynthiajoan May 28, 2026
4ab5067
update api name and parameter
cynthiajoan May 29, 2026
c0c443f
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan May 29, 2026
d1c4187
pigeon update
cynthiajoan May 29, 2026
b8f8b76
setup for web, need https://github.com/firebase/flutterfire/pull/1833…
cynthiajoan Jun 2, 2026
73e3e11
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan Jun 2, 2026
c0ea605
make the web work with default rCE provider
cynthiajoan Jun 3, 2026
98e3c43
Add recaptcha sitekey from options to native sdk
cynthiajoan Jun 3, 2026
6e7bf9a
add test for option recaptchaSiteKey
cynthiajoan Jun 3, 2026
08bb9c9
address code review
cynthiajoan Jun 3, 2026
fe7e987
update the example UI
cynthiajoan Jun 4, 2026
58d9714
only make the new web provider const
cynthiajoan Jun 4, 2026
ba36a84
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan Jun 10, 2026
686d6d4
Merge branch 'main' into firebaseai/appcheck_research
cynthiajoan Jun 12, 2026
4d2eee7
some clean up in gradle
cynthiajoan Jun 12, 2026
cc71770
change the import of recaptcha from implementation to compileOnly
cynthiajoan Jun 14, 2026
b6ccc1c
don't need appcheck example update for this PR
cynthiajoan Jun 14, 2026
c5e3a29
fix analyze and format (some)
cynthiajoan Jun 15, 2026
8c1faa8
not use compileOnly, but instead actually depend on appcheck-recaptcha
cynthiajoan Jun 15, 2026
39697bc
fix the format of pigeon generated files
cynthiajoan Jun 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ android {
implementation platform("com.google.firebase:firebase-bom:${getRootProjectExtOrCoreProperty("FirebaseSDKVersion", firebaseCoreProject)}")
implementation 'com.google.firebase:firebase-appcheck-debug'
implementation 'com.google.firebase:firebase-appcheck-playintegrity'
implementation 'com.google.firebase:firebase-appcheck-recaptcha'
implementation 'androidx.annotation:annotation:1.7.0'
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.google.firebase.FirebaseApp
import com.google.firebase.appcheck.FirebaseAppCheck
import com.google.firebase.appcheck.debug.DebugAppCheckProviderFactory
import com.google.firebase.appcheck.playintegrity.PlayIntegrityAppCheckProviderFactory
import com.google.firebase.appcheck.recaptcha.RecaptchaAppCheckProviderFactory
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.plugin.common.BinaryMessenger
Expand Down Expand Up @@ -62,6 +63,10 @@ class FirebaseAppCheckPlugin : FlutterFirebasePlugin, FlutterPlugin, FirebaseApp
firebaseAppCheck.installAppCheckProviderFactory(
DebugAppCheckProviderFactory.getInstance())
}
"recaptcha" -> {
firebaseAppCheck.installAppCheckProviderFactory(
RecaptchaAppCheckProviderFactory.getInstance())
}
else -> {
firebaseAppCheck.installAppCheckProviderFactory(
PlayIntegrityAppCheckProviderFactory.getInstance())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2025, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")

Expand Down Expand Up @@ -44,7 +44,7 @@ class FlutterError(
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
) : RuntimeException()

private open class GeneratedAndroidFirebaseAppCheckPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2025, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Autogenerated from Pigeon (v25.3.2), do not edit directly.
// Autogenerated from Pigeon (v26.3.4), do not edit directly.
// See also: https://pub.dev/packages/pigeon

import Foundation
Expand All @@ -27,12 +27,13 @@ final class PigeonError: Error {
}

var localizedDescription: String {
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
return
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
}
}

private func wrapResult(_ result: Any?) -> [Any?] {
[result]
return [result]
}

private func wrapError(_ error: Any) -> [Any?] {
Expand All @@ -52,66 +53,60 @@ private func wrapError(_ error: Any) -> [Any?] {
}
return [
"\(error)",
"\(type(of: error))",
"\(Swift.type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}

private func isNullish(_ value: Any?) -> Bool {
value is NSNull || value == nil
return value is NSNull || value == nil
}

private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
}

private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader {}
private class FirebaseAppCheckMessagesPigeonCodecReader: FlutterStandardReader {
}

private class FirebaseAppCheckMessagesPigeonCodecWriter: FlutterStandardWriter {}
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, Error>) -> Void)
appName: String, androidProvider: String?, appleProvider: String?, debugToken: String?,
completion: @escaping (Result<Void, Error>) -> Void)
func getToken(
appName: String, forceRefresh: Bool,
completion: @escaping (Result<String?, Error>) -> Void)
appName: String, forceRefresh: Bool, completion: @escaping (Result<String?, Error>) -> Void)
func setTokenAutoRefreshEnabled(
appName: String, isTokenAutoRefreshEnabled: Bool,
completion: @escaping (Result<Void, Error>) -> Void)
func registerTokenListener(appName: String, completion: @escaping (Result<String, Error>) -> Void)
func getLimitedUseAppCheckToken(
appName: String,
completion: @escaping (Result<String, Error>) -> Void)
appName: String, completion: @escaping (Result<String, Error>) -> 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 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 = ""
Expand All @@ -120,20 +115,16 @@ class FirebaseAppCheckHostApiSetup {
let activateChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.activate\(channelSuffix)",
binaryMessenger: binaryMessenger,
codec: codec
)
if let api {
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,
appName: appNameArg, androidProvider: androidProviderArg, appleProvider: appleProviderArg,
debugToken: debugTokenArg
) { result in
switch result {
Expand All @@ -150,10 +141,8 @@ class FirebaseAppCheckHostApiSetup {
let getTokenChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getToken\(channelSuffix)",
binaryMessenger: binaryMessenger,
codec: codec
)
if let api {
binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getTokenChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let appNameArg = args[0] as! String
Expand All @@ -173,17 +162,14 @@ class FirebaseAppCheckHostApiSetup {
let setTokenAutoRefreshEnabledChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.setTokenAutoRefreshEnabled\(channelSuffix)",
binaryMessenger: binaryMessenger,
codec: codec
)
if let api {
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
appName: appNameArg, isTokenAutoRefreshEnabled: isTokenAutoRefreshEnabledArg
) { result in
switch result {
case .success:
Expand All @@ -199,10 +185,8 @@ class FirebaseAppCheckHostApiSetup {
let registerTokenListenerChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.registerTokenListener\(channelSuffix)",
binaryMessenger: binaryMessenger,
codec: codec
)
if let api {
binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
registerTokenListenerChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let appNameArg = args[0] as! String
Expand All @@ -221,10 +205,8 @@ class FirebaseAppCheckHostApiSetup {
let getLimitedUseAppCheckTokenChannel = FlutterBasicMessageChannel(
name:
"dev.flutter.pigeon.firebase_app_check_platform_interface.FirebaseAppCheckHostApi.getLimitedUseAppCheckToken\(channelSuffix)",
binaryMessenger: binaryMessenger,
codec: codec
)
if let api {
binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
getLimitedUseAppCheckTokenChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let appNameArg = args[0] as! String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ public class FirebaseAppCheckPlugin: NSObject, FlutterPlugin,
}
let provider = appleProvider ?? "deviceCheck"

providerFactory?.configure(app: app, providerName: provider, debugToken: debugToken)
providerFactory?.configure(
app: app,
providerName: provider,
debugToken: debugToken
)

completion(.success(()))
}
Expand Down Expand Up @@ -192,7 +196,7 @@ public class FirebaseAppCheckPlugin: NSObject, FlutterPlugin,
channel.setStreamHandler(nil)
}
for (_, handler) in streamHandlers {
handler.onCancel(withArguments: nil)
_ = handler.onCancel(withArguments: nil)
}
eventChannels.removeAll()
streamHandlers.removeAll()
Expand Down Expand Up @@ -277,24 +281,40 @@ class FlutterAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
if providers[app.name] == nil {
let wrapper = AppCheckProviderWrapper()
// Default to deviceCheck. activate() will reconfigure with the correct provider.
wrapper.configure(app: app, providerName: "deviceCheck", debugToken: nil)
wrapper.configure(
app: app,
providerName: "deviceCheck",
debugToken: nil
)
providers[app.name] = wrapper
}
return providers[app.name]
}

func configure(app: FirebaseApp, providerName: String, debugToken: String?) {
func configure(
app: FirebaseApp,
providerName: String,
debugToken: String?
) {
if providers[app.name] == nil {
providers[app.name] = AppCheckProviderWrapper()
}
providers[app.name]?.configure(app: app, providerName: providerName, debugToken: debugToken)
providers[app.name]?.configure(
app: app,
providerName: providerName,
debugToken: debugToken
)
}
}

class AppCheckProviderWrapper: NSObject, AppCheckProvider {
private var delegateProvider: (any AppCheckProvider)?

func configure(app: FirebaseApp, providerName: String, debugToken: String?) {
func configure(
app: FirebaseApp,
providerName: String,
debugToken: String?
) {
switch providerName {
case "debug":
if let debugToken {
Expand All @@ -316,6 +336,17 @@ class AppCheckProviderWrapper: NSObject, AppCheckProvider {
} else {
delegateProvider = DeviceCheckProvider(app: app)
}
case "recaptcha":
#if os(iOS)
delegateProvider = RecaptchaProvider(app: app)
if delegateProvider == nil {
print(
"Firebase App Check: failed to initialize RecaptchaProvider. Ensure site key is in GoogleService-Info.plist."
)
}
#else
print("Firebase App Check: reCAPTCHA is only supported on iOS.")
#endif
default:
// deviceCheck
delegateProvider = DeviceCheckProvider(app: app)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ export 'package:firebase_app_check_platform_interface/firebase_app_check_platfor
AndroidAppCheckProvider,
AndroidDebugProvider,
AndroidPlayIntegrityProvider,
AndroidReCaptchaProvider,
AppleProvider,
AppleAppCheckProvider,
AppleDebugProvider,
AppleDeviceCheckProvider,
AppleAppAttestProvider,
AppleAppAttestWithDeviceCheckFallbackProvider,
AppleReCaptchaProvider,
ReCaptchaEnterpriseProvider,
ReCaptchaV3Provider,
WebDebugProvider,
WebProvider,
WebReCaptchaProvider,
Comment thread
cynthiajoan marked this conversation as resolved.
WindowsAppCheckProvider,
WindowsDebugProvider;
export 'package:firebase_core_platform_interface/firebase_core_platform_interface.dart'
Expand Down
Loading
Loading