diff --git a/app/src/internal/FirebaseInterops.cs b/app/src/internal/FirebaseInterops.cs index 11a59a67b..c880f18fb 100755 --- a/app/src/internal/FirebaseInterops.cs +++ b/app/src/internal/FirebaseInterops.cs @@ -32,6 +32,7 @@ internal static class FirebaseInterops private static Type _appCheckType; private static MethodInfo _appCheckGetInstanceMethod; private static MethodInfo _appCheckGetTokenMethod; + private static MethodInfo _appCheckGetLimitedUseTokenMethod; private static PropertyInfo _appCheckTokenResultProperty; private static PropertyInfo _appCheckTokenTokenProperty; // Used to determine if the App Check reflection initialized successfully, and should work. @@ -153,6 +154,7 @@ private static void InitializeAppCheckReflection() { const string firebaseAppCheckTypeName = "Firebase.AppCheck.FirebaseAppCheck, Firebase.AppCheck"; const string getAppCheckTokenMethodName = "GetAppCheckTokenAsync"; + const string getLimitedUseAppCheckTokenMethodName = "GetLimitedUseAppCheckTokenAsync"; try { @@ -185,6 +187,16 @@ private static void InitializeAppCheckReflection() return; } + // Get the instance method GetLimitedUseAppCheckTokenAsync() + _appCheckGetLimitedUseTokenMethod = _appCheckType.GetMethod( + getLimitedUseAppCheckTokenMethodName, BindingFlags.Instance | BindingFlags.Public, null, + Type.EmptyTypes, null); + if (_appCheckGetLimitedUseTokenMethod == null) + { + LogError($"Could not find {getLimitedUseAppCheckTokenMethodName} method via reflection."); + return; + } + // Should be Task Type appCheckTokenTaskType = _appCheckGetTokenMethod.ReturnType; @@ -215,7 +227,7 @@ private static void InitializeAppCheckReflection() } // Gets the AppCheck Token, assuming there is one. Otherwise, returns null. - internal static async Task GetAppCheckTokenAsync(FirebaseApp firebaseApp) + internal static async Task GetAppCheckTokenAsync(FirebaseApp firebaseApp, bool limitedUse = false) { // If AppCheck reflection failed for any reason, nothing to do. if (!_appCheckReflectionInitialized) @@ -233,8 +245,17 @@ internal static async Task GetAppCheckTokenAsync(FirebaseApp firebaseApp return null; } - // Invoke GetAppCheckTokenAsync(false) - returns a Task - object taskObject = _appCheckGetTokenMethod.Invoke(appCheckInstance, new object[] { false }); + object taskObject; + if (limitedUse) + { + taskObject = _appCheckGetLimitedUseTokenMethod.Invoke(appCheckInstance, null); + } + else + { + // Invoke GetAppCheckTokenAsync(false) - returns a Task + taskObject = _appCheckGetTokenMethod.Invoke(appCheckInstance, new object[] { false }); + } + if (taskObject is not Task appCheckTokenTask) { LogError($"Invoking GetToken did not return a Task."); @@ -260,7 +281,8 @@ internal static async Task GetAppCheckTokenAsync(FirebaseApp firebaseApp } // Get the Token property from the AppCheckToken struct - return _appCheckTokenTokenProperty.GetValue(tokenResult) as string; + string finalToken = _appCheckTokenTokenProperty.GetValue(tokenResult) as string; + return finalToken; } catch (Exception e) { @@ -404,9 +426,9 @@ internal static async Task GetAuthTokenAsync(FirebaseApp firebaseApp) } // Adds the other Firebase tokens to the HttpRequest, as available. - internal static async Task AddFirebaseTokensAsync(HttpRequestMessage request, FirebaseApp firebaseApp, string authTokenPrefix = "Firebase") + internal static async Task AddFirebaseTokensAsync(HttpRequestMessage request, FirebaseApp firebaseApp, string authTokenPrefix = "Firebase", bool limitedUseAppCheckTokens = false) { - string appCheckToken = await GetAppCheckTokenAsync(firebaseApp); + string appCheckToken = await GetAppCheckTokenAsync(firebaseApp, limitedUseAppCheckTokens); if (!string.IsNullOrEmpty(appCheckToken)) { request.Headers.Add(appCheckHeader, appCheckToken); diff --git a/app/src/internal/HttpHelpers.cs b/app/src/internal/HttpHelpers.cs index 917069fdd..69de60a17 100755 --- a/app/src/internal/HttpHelpers.cs +++ b/app/src/internal/HttpHelpers.cs @@ -24,7 +24,7 @@ namespace Firebase.Internal // Helper functions to help handling the Http calls. internal static class HttpHelpers { - internal static async Task SetRequestHeaders(HttpRequestMessage request, FirebaseApp firebaseApp, string authPrefix = "Firebase") + internal static async Task SetRequestHeaders(HttpRequestMessage request, FirebaseApp firebaseApp, string authPrefix = "Firebase", bool limitedUseAppCheckTokens = false) { request.Headers.Add("x-goog-api-key", firebaseApp.Options.ApiKey); string version = FirebaseInterops.GetVersionInfoSdkVersion(); @@ -35,7 +35,7 @@ internal static async Task SetRequestHeaders(HttpRequestMessage request, Firebas request.Headers.Add("X-Firebase-AppVersion", UnityEngine.Application.version); } // Add additional Firebase tokens to the header. - await FirebaseInterops.AddFirebaseTokensAsync(request, firebaseApp, authPrefix); + await FirebaseInterops.AddFirebaseTokensAsync(request, firebaseApp, authPrefix, limitedUseAppCheckTokens); } // Helper function to throw an exception if the Http Response indicates failure. diff --git a/docs/readme.md b/docs/readme.md index 062960732..7d4dc30ab 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -121,6 +121,7 @@ Release Notes - Storage: Added `ListAsync` API to list items and prefixes under a reference. - Functions: Fixed tgz export, added missing asmdef for functions. Fixes issue where Functions were not being exported correctly in the tgz build. - Firebase AI: Fix tgz export, added missing asmdef for Firebase AI. Fixes issue where Firebase AI was not being exported correctly in the tgz build. + - Functions: Added support for passing and enforcing Limited Use App Check tokens. ### 13.10.0 - Changes diff --git a/functions/src/FirebaseFunctions.cs b/functions/src/FirebaseFunctions.cs index bab1cc27a..74d7f30b0 100644 --- a/functions/src/FirebaseFunctions.cs +++ b/functions/src/FirebaseFunctions.cs @@ -228,22 +228,22 @@ private string GetUrl(in string name) { /// /// Creates a given a name. /// - public HttpsCallableReference GetHttpsCallable(string name) { - return new HttpsCallableReference(this, GetUrl(name)); + public HttpsCallableReference GetHttpsCallable(string name, HttpsCallableOptions options = null) { + return new HttpsCallableReference(this, GetUrl(name), options); } /// /// Creates a given a URL. /// - public HttpsCallableReference GetHttpsCallableFromURL(string url) { - return new HttpsCallableReference(this, url); + public HttpsCallableReference GetHttpsCallableFromURL(string url, HttpsCallableOptions options = null) { + return new HttpsCallableReference(this, url, options); } /// /// Creates a given a URL. /// - public HttpsCallableReference GetHttpsCallableFromURL(Uri url) { - return GetHttpsCallableFromURL(url.ToString()); + public HttpsCallableReference GetHttpsCallableFromURL(Uri url, HttpsCallableOptions options = null) { + return GetHttpsCallableFromURL(url.ToString(), options); } /// diff --git a/functions/src/HttpsCallableOptions.cs b/functions/src/HttpsCallableOptions.cs new file mode 100644 index 000000000..d54e2df9a --- /dev/null +++ b/functions/src/HttpsCallableOptions.cs @@ -0,0 +1,30 @@ +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace Firebase.Functions +{ + /// + /// Options to configure a Callable Function reference. + /// + public sealed class HttpsCallableOptions + { + /// + /// If set to true, uses limited use App Check token for callable function requests from this + /// instance of Functions. By default, this is false. + /// + public bool LimitedUseAppCheckTokens { get; set; } + } +} diff --git a/functions/src/HttpsCallableOptions.cs.meta b/functions/src/HttpsCallableOptions.cs.meta new file mode 100644 index 000000000..ead48d8ba --- /dev/null +++ b/functions/src/HttpsCallableOptions.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: adaffa7f07b04ac5aae3cd048ec370ad +timeCreated: 1480838400 +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/functions/src/HttpsCallableReference.cs b/functions/src/HttpsCallableReference.cs index 47eb19ee7..f967747fb 100644 --- a/functions/src/HttpsCallableReference.cs +++ b/functions/src/HttpsCallableReference.cs @@ -33,13 +33,15 @@ public sealed class HttpsCallableReference // Functions object this reference was created from. private readonly FirebaseFunctions _firebaseFunctions; private readonly string _url; + private readonly HttpsCallableOptions _options; /// /// Construct a wrapper around the HttpsCallableReferenceInternal object. /// - internal HttpsCallableReference(FirebaseFunctions functions, string url) + internal HttpsCallableReference(FirebaseFunctions functions, string url, HttpsCallableOptions options = null) { _firebaseFunctions = functions; _url = url; + _options = options; } /// @@ -90,7 +92,8 @@ private async Task InternalCallAsync(object data) HttpRequestMessage request = new(HttpMethod.Post, _url); // Functions uses Bearer tokens for authentication. // This is different from the default Firebase token prefix used by other Firebase services. - await HttpHelpers.SetRequestHeaders(request, _firebaseFunctions.App, "Bearer"); + bool limitedUseAppCheckTokens = _options != null && _options.LimitedUseAppCheckTokens; + await HttpHelpers.SetRequestHeaders(request, _firebaseFunctions.App, "Bearer", limitedUseAppCheckTokens); request.Content = MakeFunctionsRequest(data); #if FIREBASE_LOG_REST_CALLS diff --git a/functions/testapp/Assets/Firebase/Sample/Functions/TestCase.cs b/functions/testapp/Assets/Firebase/Sample/Functions/TestCase.cs index ddcaa26a0..e6d55c087 100644 --- a/functions/testapp/Assets/Firebase/Sample/Functions/TestCase.cs +++ b/functions/testapp/Assets/Firebase/Sample/Functions/TestCase.cs @@ -20,9 +20,12 @@ namespace Firebase.Sample.Functions { using System.Threading.Tasks; public class TestCase { - // The name of the HTTPS callable function to call. + // The display name of the test. public string Name { get; private set; } + // The name of the HTTPS callable function to call. + public string FunctionName { get; private set; } + // The parameters to pass to the function. public object Input { get; private set; } @@ -32,18 +35,24 @@ public class TestCase { // The error code expected to be returned from the function. public FunctionsErrorCode ExpectedError { get; private set; } - public TestCase(string name, object input, object expectedResult, - FunctionsErrorCode expectedError = FunctionsErrorCode.None) { + // The options to pass to the function. + public HttpsCallableOptions Options { get; private set; } + + public TestCase(string name, string functionName, object input, object expectedResult, + FunctionsErrorCode expectedError = FunctionsErrorCode.None, + HttpsCallableOptions options = null) { Name = name; + FunctionName = functionName; Input = input; ExpectedData = expectedResult; ExpectedError = expectedError; + Options = options; } // Returns the CallableReference to be used by the test. Overridable to allow // different ways to generate the CallableReference. public virtual HttpsCallableReference GetReference(FirebaseFunctions functions) { - return functions.GetHttpsCallable(Name); + return functions.GetHttpsCallable(FunctionName, Options); } // Runs the given test and returns whether it passed. @@ -96,13 +105,13 @@ public class TestCaseWithURL : TestCase { public TestCaseWithURL(string name, System.Uri url, object input, object expectedResult, FunctionsErrorCode expectedError = FunctionsErrorCode.None) - : base(name, input, expectedResult, expectedError) { + : base(name, url.ToString(), input, expectedResult, expectedError) { URL = url; } // Generate the CallableReference using the URL public override HttpsCallableReference GetReference(FirebaseFunctions functions) { - return functions.GetHttpsCallableFromURL(URL); + return functions.GetHttpsCallableFromURL(URL, Options); } } } diff --git a/functions/testapp/Assets/Firebase/Sample/Functions/UIHandler.cs b/functions/testapp/Assets/Firebase/Sample/Functions/UIHandler.cs index d1b0056c3..fea1d2c04 100644 --- a/functions/testapp/Assets/Firebase/Sample/Functions/UIHandler.cs +++ b/functions/testapp/Assets/Firebase/Sample/Functions/UIHandler.cs @@ -16,6 +16,7 @@ namespace Firebase.Sample.Functions { using Firebase; using Firebase.Extensions; using Firebase.Functions; + using System; using System.Collections; using System.Collections.Generic; @@ -35,10 +36,12 @@ public class UIHandler : MonoBehaviour { private DependencyStatus dependencyStatus = DependencyStatus.UnavailableOther; protected FirebaseFunctions functions; + // When the app starts, check to make sure that we have // the required dependencies to use Firebase, and if not, // add them if possible. protected virtual void Start() { + FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => { dependencyStatus = task.Result; if (dependencyStatus == DependencyStatus.Available) { @@ -50,6 +53,8 @@ protected virtual void Start() { }); } + + protected virtual void InitializeFirebase() { functions = FirebaseFunctions.DefaultInstance; UIEnabled = true; @@ -93,6 +98,7 @@ protected void GUIDisplayTests() { if (GUILayout.Button("addNumbers")) { StartCoroutine(AddNumbers(5, 7)); } + GUILayout.EndVertical(); } @@ -117,6 +123,8 @@ protected IEnumerator AddNumbers(int firstNumber, int secondNumber) { yield return new WaitUntil(() => task.IsCompleted); } + + // Render the buttons and other controls. void GUIDisplayControls() { if (UIEnabled) { diff --git a/functions/testapp/Assets/Firebase/Sample/Functions/UIHandlerAutomated.cs b/functions/testapp/Assets/Firebase/Sample/Functions/UIHandlerAutomated.cs index f4b377231..08f4fcfc5 100644 --- a/functions/testapp/Assets/Firebase/Sample/Functions/UIHandlerAutomated.cs +++ b/functions/testapp/Assets/Firebase/Sample/Functions/UIHandlerAutomated.cs @@ -1,4 +1,6 @@ namespace Firebase.Sample.Functions { + using Firebase; + using Firebase.AppCheck; using Firebase.Extensions; using Firebase.Functions; using System; @@ -10,6 +12,7 @@ namespace Firebase.Sample.Functions { public class UIHandlerAutomated : UIHandler { private Firebase.Sample.AutomatedTestRunner testRunner; private Firebase.Auth.FirebaseAuth firebaseAuth; + private string appCheckDebugTokenForAutomated = "REPLACE_WITH_APP_CHECK_TOKEN"; // Returns the set of all integration tests. public static IEnumerable AllTests() { @@ -35,24 +38,37 @@ public static IEnumerable AllTests() { expectedArray.Add(3L); expected["array"] = expectedArray; - yield return new TestCase("dataTest", data, expected); + yield return new TestCase("dataTest", "dataTest", data, expected); + } + + { + var data = new Dictionary(); + data["firstNumber"] = 5; + data["secondNumber"] = 7; + var expected = new Dictionary(); + expected["firstNumber"] = 5L; + expected["secondNumber"] = 7L; + expected["operator"] = "+"; + expected["operationResult"] = 12L; + yield return new TestCase("addNumbersWithLimitedUse", "addNumbers", data, expected, FunctionsErrorCode.None, new HttpsCallableOptions { LimitedUseAppCheckTokens = true }); } var empty = new Dictionary(); - yield return new TestCase("scalarTest", 17, 76L); - yield return new TestCase("tokenTest", empty, empty); + yield return new TestCase("scalarTest", "scalarTest", 17, 76L); + yield return new TestCase("scalarTestwithLimitedUse", "scalarTest", 17, 76L, FunctionsErrorCode.None, new HttpsCallableOptions { LimitedUseAppCheckTokens = true }); + yield return new TestCase("tokenTest", "tokenTest", empty, empty); // Only run this on iOS and Android. - // yield return new TestCase("instanceIdTest", empty, empty); - yield return new TestCase("nullTest", null, null); + // yield return new TestCase("instanceIdTest", "instanceIdTest", empty, empty); + yield return new TestCase("nullTest", "nullTest", null, null); // Test various error cases. - yield return new TestCase("missingResultTest", null, null, + yield return new TestCase("missingResultTest", "missingResultTest", null, null, FunctionsErrorCode.Internal); - yield return new TestCase("unhandledErrorTest", null, null, + yield return new TestCase("unhandledErrorTest", "unhandledErrorTest", null, null, FunctionsErrorCode.Internal); - yield return new TestCase("unknownErrorTest", null, null, + yield return new TestCase("unknownErrorTest", "unknownErrorTest", null, null, FunctionsErrorCode.Internal); - yield return new TestCase("explicitErrorTest", null, null, + yield return new TestCase("explicitErrorTest", "explicitErrorTest", null, null, FunctionsErrorCode.OutOfRange); // Test calling via Url @@ -63,6 +79,7 @@ public static IEnumerable AllTests() { } protected override void Start() { + InitializeAppCheck(); // Set the list of tests to run, note this is done at Start since they are // non-static. var testCases = AllTests().ToArray(); @@ -76,9 +93,16 @@ protected override void Start() { ); UIEnabled = false; + base.Start(); } + protected void InitializeAppCheck() { + DebugLog("Initializing App Check directly in automated handler"); + DebugAppCheckProviderFactory.Instance.SetDebugToken(appCheckDebugTokenForAutomated); + FirebaseAppCheck.SetAppCheckProviderFactory(DebugAppCheckProviderFactory.Instance); + } + protected override void InitializeFirebase() { // One of the automated tests requires Auth, so we want to sign in before running it. firebaseAuth = Firebase.Auth.FirebaseAuth.DefaultInstance; diff --git a/functions/testapp/functions/functions/index.js b/functions/testapp/functions/functions/index.js new file mode 100644 index 000000000..fe470e646 --- /dev/null +++ b/functions/testapp/functions/functions/index.js @@ -0,0 +1,43 @@ +// Copyright 2026 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +const functions = require("firebase-functions"); + +// Creates a function that consumes limited-use App Check tokens +exports.addtwowithlimiteduse = functions.runWith({ + enforceAppCheck: true, + consumeAppCheckToken: true, + maxInstances: 10 // Setting maxInstances the V1 way +}).https.onCall((data, context) => { + // context.app will be defined if a valid App Check token was provided + if (context.app === undefined) { + throw new functions.https.HttpsError( + 'failed-precondition', + 'The function must be called from an App Check verified app.'); + } + + const firstNumber = data.firstNumber; + const secondNumber = data.secondNumber; + + if (firstNumber === undefined || secondNumber === undefined) { + throw new functions.https.HttpsError('invalid-argument', 'The function must be called with "firstNumber" and "secondNumber".'); + } + + return { + firstNumber: firstNumber, + secondNumber: secondNumber, + operator: '+', + operationResult: Number(firstNumber) + Number(secondNumber), + }; +}); diff --git a/functions/testapp/functions/functions/package.json b/functions/testapp/functions/functions/package.json new file mode 100644 index 000000000..8725be2c1 --- /dev/null +++ b/functions/testapp/functions/functions/package.json @@ -0,0 +1,13 @@ +{ + "name": "functions", + "description": "Cloud Functions for Firebase", + "engines": { + "node": "20" + }, + "main": "index.js", + "dependencies": { + "firebase-admin": "^11.5.0", + "firebase-functions": "^4.2.1" + }, + "private": true +} diff --git a/scripts/gha/integration_testing/build_testapps.json b/scripts/gha/integration_testing/build_testapps.json index 35940ae48..d844a28d0 100644 --- a/scripts/gha/integration_testing/build_testapps.json +++ b/scripts/gha/integration_testing/build_testapps.json @@ -130,16 +130,18 @@ "captial_name": "Functions", "bundle_id": "com.google.firebase.unity.functions.testapp", "testapp_path": "functions/testapp", - "platforms": ["Android", "Playmode", "iOS", "tvOS", "Desktop"], + "platforms": ["Android", "Playmode", "iOS", "tvOS", "Windows", "macOS", "Linux"], "plugins": [ "FirebaseFunctions.unitypackage", - "FirebaseAuth.unitypackage" + "FirebaseAuth.unitypackage", + "FirebaseAppCheck.unitypackage" ], "provision": "Firebase_Dev_Wildcard.mobileprovision", "upm_packages": [ "com.google.external-dependency-manager-*.tgz", "com.google.firebase.app-*.tgz", "com.google.firebase.auth-*.tgz", + "com.google.firebase.app-check-*.tgz", "com.google.firebase.functions-*.tgz" ] }, diff --git a/scripts/gha/restore_secrets.py b/scripts/gha/restore_secrets.py index d69560b8c..7ddd0fb65 100644 --- a/scripts/gha/restore_secrets.py +++ b/scripts/gha/restore_secrets.py @@ -129,6 +129,9 @@ def main(argv): _patch_file(file_path, "REPLACE_WITH_APP_CHECK_TOKEN", debug_token) file_path = os.path.join(repo_dir, "app_check", "testapp", "Assets", "Firebase", "Sample", CAPITALIZATIONS["app_check"], "UIHandler.cs") _patch_file(file_path, "REPLACE_WITH_APP_CHECK_TOKEN", debug_token) + file_path = os.path.join(repo_dir, "functions", "testapp", "Assets", "Firebase", "Sample", CAPITALIZATIONS["functions"], "UIHandlerAutomated.cs") + _patch_file(file_path, "REPLACE_WITH_APP_CHECK_TOKEN", debug_token) + print("Attempting to decrypt GCS service account key file.") decrypted_key_file = os.path.join(secrets_dir, "gcs_key_file.json") diff --git a/unity_packer/debug_single_export_json/firebaseai.json b/unity_packer/debug_single_export_json/firebaseai.json index d4c60e8a8..136f71eab 100644 --- a/unity_packer/debug_single_export_json/firebaseai.json +++ b/unity_packer/debug_single_export_json/firebaseai.json @@ -152,6 +152,12 @@ "Firebase/Plugins/x86_64/FirebaseCppApp.*" ] }, + { + "importer": "DefaultImporter", + "paths": [ + "Firebase/FirebaseApp/Internal/" + ] + }, { "labels": [ "gvh_targets-windows-windows64-osx-osxintel-osxintel64-linux-linux32-linux64" diff --git a/unity_packer/debug_single_export_json/functions.json b/unity_packer/debug_single_export_json/functions.json index c90fb8e91..23288bb92 100644 --- a/unity_packer/debug_single_export_json/functions.json +++ b/unity_packer/debug_single_export_json/functions.json @@ -152,6 +152,12 @@ "Firebase/Plugins/x86_64/FirebaseCppApp.*" ] }, + { + "importer": "DefaultImporter", + "paths": [ + "Firebase/FirebaseApp/Internal/" + ] + }, { "labels": [ "gvh_targets-windows-windows64-osx-osxintel-osxintel64-linux-linux32-linux64" @@ -216,48 +222,9 @@ "name": "FirebaseFunctions.unitypackage", "imports": [ { - "importer": "PluginImporter", - "platforms": [ - "Editor", - "Standalone", - "Android" - ], - "cpu": "AnyCPU", - "paths": [ - "Firebase/Plugins/Firebase.Functions.dll", - "Firebase/Plugins/Firebase.Functions.pdb" - ] - }, - { - "importer": "PluginImporter", - "platforms": [ - "iOS", - "tvOS" - ], - "cpu": "AnyCPU", - "paths": [ - "Firebase/Plugins/iOS/Firebase.Functions.dll", - "Firebase/Plugins/iOS/Firebase.Functions.pdb" - ] - }, - { - "importer": "PluginImporter", - "platforms": [ - "iOS" - ], - "cpu": "AnyCPU", - "paths": [ - "Plugins/iOS/Firebase/libFirebaseCppFunctions.a" - ] - }, - { - "importer": "PluginImporter", - "platforms": [ - "tvOS" - ], - "cpu": "AnyCPU", + "importer": "DefaultImporter", "paths": [ - "Plugins/tvOS/Firebase/libFirebaseCppFunctions.a" + "Firebase/FirebaseFunctions/*" ] }, { @@ -267,17 +234,6 @@ "Firebase/m2repository/com/google/firebase/firebase-functions-unity/" ] }, - { - "importer": "PluginImporter", - "platforms": [ - "Editor", - "Standalone" - ], - "cpu": "x86_64", - "paths": [ - "Firebase/Plugins/x86_64/FirebaseCppFunctions*" - ] - }, { "importer": "DefaultImporter", "sections": [