diff --git a/packages/cloud_functions/cloud_functions/android/build.gradle b/packages/cloud_functions/cloud_functions/android/build.gradle index afd9a2296e53..0750dbb4f75f 100644 --- a/packages/cloud_functions/cloud_functions/android/build.gradle +++ b/packages/cloud_functions/cloud_functions/android/build.gradle @@ -8,7 +8,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.4' + classpath 'com.android.tools.build:gradle:8.0.1' } } @@ -39,7 +39,7 @@ android { if (project.android.hasProperty("namespace")) { namespace 'io.flutter.plugins.firebase.functions' } - + compileSdkVersion 31 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/packages/cloud_functions/cloud_functions/android/src/main/java/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.java b/packages/cloud_functions/cloud_functions/android/src/main/java/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.java index 0d40087e839f..e93575011d84 100644 --- a/packages/cloud_functions/cloud_functions/android/src/main/java/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.java +++ b/packages/cloud_functions/cloud_functions/android/src/main/java/io/flutter/plugins/firebase/functions/FlutterFirebaseFunctionsPlugin.java @@ -13,6 +13,7 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.functions.FirebaseFunctions; import com.google.firebase.functions.FirebaseFunctionsException; +import com.google.firebase.functions.HttpsCallableOptions; import com.google.firebase.functions.HttpsCallableReference; import com.google.firebase.functions.HttpsCallableResult; import io.flutter.embedding.engine.plugins.FlutterPlugin; @@ -74,6 +75,8 @@ private Task httpsFunctionCall(Map arguments) { String functionUri = (String) arguments.get("functionUri"); String origin = (String) arguments.get("origin"); Integer timeout = (Integer) arguments.get("timeout"); + boolean limitedUseAppCheckToken = + (boolean) Objects.requireNonNull(arguments.get("limitedUseAppCheckToken")); Object parameters = arguments.get("parameters"); if (origin != null) { @@ -82,12 +85,16 @@ private Task httpsFunctionCall(Map arguments) { } HttpsCallableReference httpsCallableReference; + HttpsCallableOptions options = + new HttpsCallableOptions.Builder() + .setLimitedUseAppCheckTokens(limitedUseAppCheckToken) + .build(); if (functionName != null) { - httpsCallableReference = firebaseFunctions.getHttpsCallable(functionName); + httpsCallableReference = firebaseFunctions.getHttpsCallable(functionName, options); } else if (functionUri != null) { httpsCallableReference = - firebaseFunctions.getHttpsCallableFromUrl(new URL(functionUri)); + firebaseFunctions.getHttpsCallableFromUrl(new URL(functionUri), options); } else { throw new IllegalArgumentException("Either functionName or functionUri must be set"); } diff --git a/packages/cloud_functions/cloud_functions/example/android/app/build.gradle b/packages/cloud_functions/cloud_functions/example/android/app/build.gradle index 07515cba7ca2..79b32d110890 100644 --- a/packages/cloud_functions/cloud_functions/example/android/app/build.gradle +++ b/packages/cloud_functions/cloud_functions/example/android/app/build.gradle @@ -26,13 +26,10 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { namespace 'io.flutter.plugins.firebase.functions.example' - compileSdkVersion 33 + compileSdk 33 - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { + defaultConfig { applicationId "io.flutter.plugins.firebase.functions.example" minSdkVersion 16 targetSdkVersion 33 @@ -48,6 +45,9 @@ android { signingConfig signingConfigs.debug } } + lint { + disable 'InvalidPackage' + } } flutter { diff --git a/packages/cloud_functions/cloud_functions/ios/Classes/FLTFirebaseFunctionsPlugin.m b/packages/cloud_functions/cloud_functions/ios/Classes/FLTFirebaseFunctionsPlugin.m index d106a8b4184d..187280e0330a 100644 --- a/packages/cloud_functions/cloud_functions/ios/Classes/FLTFirebaseFunctionsPlugin.m +++ b/packages/cloud_functions/cloud_functions/ios/Classes/FLTFirebaseFunctionsPlugin.m @@ -86,6 +86,7 @@ - (void)httpsFunctionCall:(id)arguments withMethodCallResult:(FLTFirebaseMethodC NSString *region = arguments[@"region"]; NSNumber *timeout = arguments[@"timeout"]; NSObject *parameters = arguments[@"parameters"]; + NSNumber *limitedUseAppCheckToken = arguments[@"limitedUseAppCheckToken"]; FIRApp *app = [FLTFirebasePlugin firebaseAppNamed:appName]; FIRFunctions *functions = [FIRFunctions functionsForApp:app region:region]; @@ -94,12 +95,15 @@ - (void)httpsFunctionCall:(id)arguments withMethodCallResult:(FLTFirebaseMethodC [functions useEmulatorWithHost:[url host] port:[[url port] intValue]]; } + FIRHTTPSCallableOptions *options = [[FIRHTTPSCallableOptions alloc] + initWithRequireLimitedUseAppCheckTokens:[limitedUseAppCheckToken boolValue]]; + FIRHTTPSCallable *function; if (![functionName isEqual:[NSNull null]]) { - function = [functions HTTPSCallableWithName:functionName]; + function = [functions HTTPSCallableWithName:functionName options:options]; } else if (![functionUri isEqual:[NSNull null]]) { - function = [functions HTTPSCallableWithURL:[NSURL URLWithString:functionUri]]; + function = [functions HTTPSCallableWithURL:[NSURL URLWithString:functionUri] options:options]; } else { result.error(@"IllegalArgumentException", @"Either functionName or functionUri must be set", nil, nil); diff --git a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/https_callable_options.dart b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/https_callable_options.dart index 9eef0d693684..37a240052a31 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/https_callable_options.dart +++ b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/https_callable_options.dart @@ -5,10 +5,16 @@ /// Interface representing an HttpsCallable instance's options, class HttpsCallableOptions { - /// Constructs a new [HttpsCallableOptions] instance with given timeout. + /// Constructs a new [HttpsCallableOptions] instance with given `timeout` & `limitedUseAppCheckToken` /// Defaults [timeout] to 60 seconds. - HttpsCallableOptions({this.timeout = const Duration(seconds: 60)}); + /// Defaults [limitedUseAppCheckToken] to `false` + HttpsCallableOptions( + {this.timeout = const Duration(seconds: 60), + this.limitedUseAppCheckToken = false}); /// Returns the timeout for this instance Duration timeout; + + /// Sets whether or not to use limited-use App Check tokens when invoking the associated function. + bool limitedUseAppCheckToken; } diff --git a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/method_channel/method_channel_https_callable.dart b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/method_channel/method_channel_https_callable.dart index dbb65f4e47f3..fb17e3e5b706 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/lib/src/method_channel/method_channel_https_callable.dart +++ b/packages/cloud_functions/cloud_functions_platform_interface/lib/src/method_channel/method_channel_https_callable.dart @@ -28,6 +28,7 @@ class MethodChannelHttpsCallable extends HttpsCallablePlatform { 'region': functions.region, 'timeout': options.timeout.inMilliseconds, 'parameters': parameters, + 'limitedUseAppCheckToken': options.limitedUseAppCheckToken, }); if (result is Map) { diff --git a/packages/cloud_functions/cloud_functions_platform_interface/test/method_channel/method_channel_https_callable_test.dart b/packages/cloud_functions/cloud_functions_platform_interface/test/method_channel/method_channel_https_callable_test.dart index 9044502eecd7..a28582b5fdfe 100644 --- a/packages/cloud_functions/cloud_functions_platform_interface/test/method_channel/method_channel_https_callable_test.dart +++ b/packages/cloud_functions/cloud_functions_platform_interface/test/method_channel/method_channel_https_callable_test.dart @@ -89,6 +89,7 @@ void main() { expect(httpsCallable!.options, isInstanceOf()); expect(httpsCallable!.options.timeout, isInstanceOf()); expect(httpsCallable!.options.timeout.inMinutes, 1); + expect(httpsCallable!.options.limitedUseAppCheckToken, false); }); }); @@ -119,6 +120,7 @@ void main() { 'region': functions!.region, 'timeout': httpsCallable!.options.timeout.inMilliseconds, 'parameters': kParameters, + 'limitedUseAppCheckToken': false, }, ), ]); @@ -149,6 +151,7 @@ void main() { 'region': functions!.region, 'timeout': httpsCallable!.options.timeout.inMilliseconds, 'parameters': kParameters, + 'limitedUseAppCheckToken': false, }, ), ]); @@ -169,6 +172,7 @@ void main() { 'region': functions!.region, 'timeout': httpsCallable!.options.timeout.inMilliseconds, 'parameters': null, + 'limitedUseAppCheckToken': false, }, ), ]); @@ -189,6 +193,7 @@ void main() { 'region': functions!.region, 'timeout': httpsCallable!.options.timeout.inMilliseconds, 'parameters': null, + 'limitedUseAppCheckToken': false, }, ), ]); diff --git a/packages/cloud_functions/cloud_functions_web/lib/https_callable_web.dart b/packages/cloud_functions/cloud_functions_web/lib/https_callable_web.dart index eac6d27a6286..c4460e309a29 100644 --- a/packages/cloud_functions/cloud_functions_web/lib/https_callable_web.dart +++ b/packages/cloud_functions/cloud_functions_web/lib/https_callable_web.dart @@ -30,7 +30,9 @@ class HttpsCallableWeb extends HttpsCallablePlatform { functions_interop.HttpsCallableOptions callableOptions = functions_interop.HttpsCallableOptions( - timeout: options.timeout.inMilliseconds); + timeout: options.timeout.inMilliseconds, + limitedUseAppCheckTokens: options.limitedUseAppCheckToken, + ); late functions_interop.HttpsCallable callable; diff --git a/packages/cloud_functions/cloud_functions_web/lib/interop/functions_interop.dart b/packages/cloud_functions/cloud_functions_web/lib/interop/functions_interop.dart index a614e1deabe1..abb3485449c3 100644 --- a/packages/cloud_functions/cloud_functions_web/lib/interop/functions_interop.dart +++ b/packages/cloud_functions/cloud_functions_web/lib/interop/functions_interop.dart @@ -44,9 +44,12 @@ abstract class FunctionsJsImpl { @JS('HttpsCallableOptions') @anonymous abstract class HttpsCallableOptions { - external factory HttpsCallableOptions({int? timeout}); + external factory HttpsCallableOptions( + {int? timeout, bool? limitedUseAppCheckTokens}); external int get timeout; external set timeout(int t); + external bool get limitedUseAppCheckTokens; + external set limitedUseAppCheckTokens(bool t); } /// An HttpsCallableResult wraps a single result from a function call. diff --git a/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart b/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart index 3e17093a0b53..20b37235bc11 100644 --- a/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart +++ b/tests/integration_test/cloud_functions/cloud_functions_e2e_test.dart @@ -227,6 +227,21 @@ void main() { // https://github.com/firebase/flutterfire/issues/9652 skip: defaultTargetPlatform == TargetPlatform.android, ); + + test( + 'allow passing of `limitedUseAppCheckToken` as option', + () async { + final instance = FirebaseFunctions.instance; + instance.useFunctionsEmulator('localhost', 5001); + final timeoutCallable = FirebaseFunctions.instance.httpsCallable( + kTestFunctionDefaultRegion, + options: HttpsCallableOptions(timeout: const Duration(seconds: 3), limitedUseAppCheckToken: true), + ); + + HttpsCallableResult results = await timeoutCallable(null); + expect(results.data, equals('null')); + }, + ); }); }); }