From ff5a7c530b10deb36c5bd1eca1bc7d9d9434c4d3 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 17 Feb 2026 16:27:09 -0800 Subject: [PATCH 1/6] Update packages for googleapis_auth breaking changes Accommodate breaking changes from googleapis_auth, specifically the removal of the `serviceAccountCredentials` getter and the refactoring of cryptographic signing. - Update `AuthClient.sign()` usages across dart_firebase_admin and googleapis_storage. The method now returns a `Future` (base64-encoded signature) instead of an object with a `signedBlob` property. - Explicitly pass `serviceAccountCredentials` into `sign()` invocations where available to maintain local RSA signing capabilities. - Remove `serviceAccountCredentials` overrides from EmulatorClient implementations in dart_firebase_admin, googleapis_storage, and googleapis_firestore. - Update Compute Engine environment detection in FunctionsRequestHandler to check internal credential options instead of the removed AuthClient.serviceAccountCredentials. - Delete local AuthExtension helpers in dart_firebase_admin and googleapis_storage, migrating to the newly provided AuthClientSigningExtension methods. - Update authClient.getServiceAccountEmail usages from a getter to a method invocation. --- packages/dart_firebase_admin/lib/src/app.dart | 1 + .../lib/src/app/emulator_client.dart | 8 -------- .../lib/src/app/firebase_app.dart | 2 +- .../lib/src/app_check/token_generator.dart | 9 ++++++--- packages/dart_firebase_admin/lib/src/auth.dart | 1 - .../lib/src/auth/token_generator.dart | 8 ++++++-- .../lib/src/functions/functions.dart | 1 - .../src/functions/functions_request_handler.dart | 10 ++++------ .../lib/src/utils/auth_extension.dart | 16 ---------------- .../lib/src/firestore_http_client.dart | 7 ++----- .../lib/googleapis_storage.dart | 1 - packages/googleapis_storage/lib/src/file.dart | 2 +- .../lib/src/internal/emulator_client.dart | 4 ---- packages/googleapis_storage/lib/src/signer.dart | 4 ++-- .../lib/src/utils/auth_extension.dart | 7 ------- pubspec.yaml | 4 ++++ 16 files changed, 27 insertions(+), 58 deletions(-) delete mode 100644 packages/dart_firebase_admin/lib/src/utils/auth_extension.dart delete mode 100644 packages/googleapis_storage/lib/src/utils/auth_extension.dart diff --git a/packages/dart_firebase_admin/lib/src/app.dart b/packages/dart_firebase_admin/lib/src/app.dart index 51f6413e..ea6792c5 100644 --- a/packages/dart_firebase_admin/lib/src/app.dart +++ b/packages/dart_firebase_admin/lib/src/app.dart @@ -6,6 +6,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:equatable/equatable.dart'; +import 'package:google_cloud/constants.dart' as google_cloud; import 'package:google_cloud/google_cloud.dart' as google_cloud; import 'package:googleapis/identitytoolkit/v3.dart' as auth3; import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; diff --git a/packages/dart_firebase_admin/lib/src/app/emulator_client.dart b/packages/dart_firebase_admin/lib/src/app/emulator_client.dart index e77822f3..ddf9ff21 100644 --- a/packages/dart_firebase_admin/lib/src/app/emulator_client.dart +++ b/packages/dart_firebase_admin/lib/src/app/emulator_client.dart @@ -35,10 +35,6 @@ class EmulatorClient extends BaseClient implements googleapis_auth.AuthClient { googleapis_auth.AccessCredentials get credentials => throw UnimplementedError(); - @override - googleapis_auth.ServiceAccountCredentials? get serviceAccountCredentials => - null; - @override Future send(BaseRequest request) async { final modifiedRequest = _RequestImpl( @@ -76,10 +72,6 @@ class CloudTasksEmulatorClient implements googleapis_auth.AuthClient { googleapis_auth.AccessCredentials get credentials => throw UnimplementedError(); - @override - googleapis_auth.ServiceAccountCredentials? get serviceAccountCredentials => - null; - /// Rewrites the URL to remove `/v2/` prefix and route to emulator host. Uri _rewriteUrl(Uri url) { // Replace the path: remove /v2/ prefix if present diff --git a/packages/dart_firebase_admin/lib/src/app/firebase_app.dart b/packages/dart_firebase_admin/lib/src/app/firebase_app.dart index 48ad70a6..41c77c89 100644 --- a/packages/dart_firebase_admin/lib/src/app/firebase_app.dart +++ b/packages/dart_firebase_admin/lib/src/app/firebase_app.dart @@ -140,7 +140,7 @@ class FirebaseApp { }) async { final env = environment ?? Zone.current[envSymbol] as Map?; if (env != null) { - for (final envKey in google_cloud.gcpProjectIdEnvironmentVariables) { + for (final envKey in google_cloud.projectIdEnvironmentVariableOptions) { final value = env[envKey]; if (value != null) return value; } diff --git a/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart b/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart index ec0dd801..2e1e03d3 100644 --- a/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart +++ b/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart @@ -4,7 +4,6 @@ import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; import 'package:meta/meta.dart'; import '../../dart_firebase_admin.dart'; -import '../utils/auth_extension.dart'; import 'app_check.dart'; import 'app_check_api.dart'; @@ -33,7 +32,7 @@ class AppCheckTokenGenerator { ]) async { try { final authClient = await app.client; - final account = authClient.getServiceAccountEmail; + final account = await authClient.getServiceAccountEmail(); final header = {'alg': 'RS256', 'typ': 'JWT'}; final iat = (DateTime.now().millisecondsSinceEpoch / 1000).floor(); @@ -48,7 +47,11 @@ class AppCheckTokenGenerator { final token = '${_encodeSegment(header)}.${_encodeSegment(body)}'; - final signature = await authClient.signBlob(utf8.encode(token)); + final signature = await authClient.sign( + utf8.encode(token), + serviceAccountCredentials: + app.options.credential?.serviceAccountCredentials, + ); return '$token.$signature'; } on googleapis_auth.ServerRequestFailedException catch (err) { diff --git a/packages/dart_firebase_admin/lib/src/auth.dart b/packages/dart_firebase_admin/lib/src/auth.dart index d7eccdf2..83502ca4 100644 --- a/packages/dart_firebase_admin/lib/src/auth.dart +++ b/packages/dart_firebase_admin/lib/src/auth.dart @@ -14,7 +14,6 @@ import 'package:meta/meta.dart'; import 'app.dart'; import 'object_utils.dart'; -import 'utils/auth_extension.dart'; import 'utils/jwt.dart'; import 'utils/utils.dart'; import 'utils/validator.dart'; diff --git a/packages/dart_firebase_admin/lib/src/auth/token_generator.dart b/packages/dart_firebase_admin/lib/src/auth/token_generator.dart index 054f551b..eeedadab 100644 --- a/packages/dart_firebase_admin/lib/src/auth/token_generator.dart +++ b/packages/dart_firebase_admin/lib/src/auth/token_generator.dart @@ -71,7 +71,7 @@ class _FirebaseTokenGenerator { try { final authClient = await _app.client; - final account = await authClient.getServiceAccountEmail; + final account = await authClient.getServiceAccountEmail(); final header = {'alg': 'RS256', 'typ': 'JWT'}; final iat = DateTime.now().millisecondsSinceEpoch ~/ 1000; @@ -87,7 +87,11 @@ class _FirebaseTokenGenerator { }; final token = '${_encodeSegment(header)}.${_encodeSegment(body)}'; - final signature = await authClient.signBlob(utf8.encode(token)); + final signature = await authClient.sign( + utf8.encode(token), + serviceAccountCredentials: + _app.options.credential?.serviceAccountCredentials, + ); return '$token.$signature'; } on googleapis_auth.ServerRequestFailedException catch (err, stack) { diff --git a/packages/dart_firebase_admin/lib/src/functions/functions.dart b/packages/dart_firebase_admin/lib/src/functions/functions.dart index fe338fed..1a5463f0 100644 --- a/packages/dart_firebase_admin/lib/src/functions/functions.dart +++ b/packages/dart_firebase_admin/lib/src/functions/functions.dart @@ -5,7 +5,6 @@ import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; import 'package:meta/meta.dart'; import '../app.dart'; -import '../utils/auth_extension.dart'; import '../utils/validator.dart'; part 'functions_api.dart'; diff --git a/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart b/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart index 4f86ea2d..1e97bf5e 100644 --- a/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart +++ b/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart @@ -253,11 +253,9 @@ class FunctionsRequestHandler { return; } - // Check if running as an extension with ComputeEngine credentials. - // ComputeEngine credentials are used when running on GCE/Cloud Run without - // a service account JSON file - indicated by credentials without local - // service account credentials (i.e., using metadata server). - final isComputeEngine = authClient.serviceAccountCredentials == null; + // Service credentials via `FirebaseApp.options`. + final isComputeEngine = + _httpClient.app.options.credential?.serviceAccountCredentials == null; if (extensionId != null && extensionId.isNotEmpty && isComputeEngine) { // Running as extension with ComputeEngine - use ID token with Authorization header. @@ -276,7 +274,7 @@ class FunctionsRequestHandler { // Default: Use OIDC token with service account email. // Try to get service account email from credential first, then from metadata service. - final serviceAccountEmail = await authClient.getServiceAccountEmail; + final serviceAccountEmail = await authClient.getServiceAccountEmail(); if (serviceAccountEmail.isEmpty) { throw FirebaseFunctionsAdminException( diff --git a/packages/dart_firebase_admin/lib/src/utils/auth_extension.dart b/packages/dart_firebase_admin/lib/src/utils/auth_extension.dart deleted file mode 100644 index c99bee95..00000000 --- a/packages/dart_firebase_admin/lib/src/utils/auth_extension.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; - -import '../app.dart'; - -extension AuthExtension on googleapis_auth.AuthClient { - Future get getServiceAccountEmail async => - serviceAccountCredentials?.email ?? - googleapis_auth.IAMSigner(this).getServiceAccountEmail(); - - /// Signs the given data using the IAM Credentials API or local credentials. - /// - /// Returns a base64-encoded signature string. In emulator mode, returns an - /// empty string to produce unsigned tokens. - Future signBlob(List data, {String? endpoint}) async => - Environment.isAuthEmulatorEnabled() ? '' : sign(data, endpoint: endpoint); -} diff --git a/packages/googleapis_firestore/lib/src/firestore_http_client.dart b/packages/googleapis_firestore/lib/src/firestore_http_client.dart index 8e99a919..f0201873 100644 --- a/packages/googleapis_firestore/lib/src/firestore_http_client.dart +++ b/packages/googleapis_firestore/lib/src/firestore_http_client.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:google_cloud/constants.dart' as google_cloud; import 'package:google_cloud/google_cloud.dart' as google_cloud; import 'package:googleapis/firestore/v1.dart' as firestore_v1; import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; @@ -45,10 +46,6 @@ class EmulatorClient extends BaseClient implements googleapis_auth.AuthClient { googleapis_auth.AccessCredentials get credentials => throw UnimplementedError(); - @override - googleapis_auth.ServiceAccountCredentials? get serviceAccountCredentials => - null; - @override Future send(BaseRequest request) async { final modifiedRequest = _RequestImpl( @@ -130,7 +127,7 @@ class FirestoreHttpClient { final env = _settings.environmentOverride; if (env != null) { - for (final envKey in google_cloud.gcpProjectIdEnvironmentVariables) { + for (final envKey in google_cloud.projectIdEnvironmentVariableOptions) { final value = env[envKey]; if (value != null) { projectId = value; diff --git a/packages/googleapis_storage/lib/googleapis_storage.dart b/packages/googleapis_storage/lib/googleapis_storage.dart index 42f94642..be023667 100644 --- a/packages/googleapis_storage/lib/googleapis_storage.dart +++ b/packages/googleapis_storage/lib/googleapis_storage.dart @@ -12,7 +12,6 @@ import 'package:googleapis_auth/auth_io.dart'; import 'package:googleapis_storage/src/internal/api_error.dart'; import 'package:googleapis_storage/src/internal/api.dart'; import 'package:googleapis_storage/src/internal/service.dart'; -import 'package:googleapis_storage/src/utils/auth_extension.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:meta/meta.dart'; diff --git a/packages/googleapis_storage/lib/src/file.dart b/packages/googleapis_storage/lib/src/file.dart index 89437c4a..34542512 100644 --- a/packages/googleapis_storage/lib/src/file.dart +++ b/packages/googleapis_storage/lib/src/file.dart @@ -1056,7 +1056,7 @@ class BucketFile extends ServiceObject // Get auth client and credentials final authClient = await storage.authClient; - final clientEmail = await authClient.getServiceAccountEmail; + final clientEmail = await authClient.getServiceAccountEmail(); // Build credential string final todayISO = _formatDateStamp(now); diff --git a/packages/googleapis_storage/lib/src/internal/emulator_client.dart b/packages/googleapis_storage/lib/src/internal/emulator_client.dart index bc41827d..7eedb3b8 100644 --- a/packages/googleapis_storage/lib/src/internal/emulator_client.dart +++ b/packages/googleapis_storage/lib/src/internal/emulator_client.dart @@ -37,10 +37,6 @@ class EmulatorClient extends BaseClient implements googleapis_auth.AuthClient { googleapis_auth.AccessCredentials get credentials => throw UnimplementedError('EmulatorClient does not provide credentials'); - @override - googleapis_auth.ServiceAccountCredentials? get serviceAccountCredentials => - null; - @override Future send(BaseRequest request) async { final modifiedRequest = _RequestImpl( diff --git a/packages/googleapis_storage/lib/src/signer.dart b/packages/googleapis_storage/lib/src/signer.dart index 2ea4f9b3..9090bedb 100644 --- a/packages/googleapis_storage/lib/src/signer.dart +++ b/packages/googleapis_storage/lib/src/signer.dart @@ -138,7 +138,7 @@ class URLSigner { endpoint: config.signedConfig.signingEndpoint?.toString(), ); - final clientEmail = await authClient.getServiceAccountEmail; + final clientEmail = await authClient.getServiceAccountEmail(); return { 'GoogleAccessId': clientEmail, @@ -202,7 +202,7 @@ class URLSigner { final authClient = await bucket.storage.authClient; - final clientEmail = await authClient.getServiceAccountEmail; + final clientEmail = await authClient.getServiceAccountEmail(); final credentialString = '$clientEmail/$credentialScope'; final dateISO = _formatAsUTCISO(config.accessibleAt, includeTime: true); diff --git a/packages/googleapis_storage/lib/src/utils/auth_extension.dart b/packages/googleapis_storage/lib/src/utils/auth_extension.dart deleted file mode 100644 index f7adb620..00000000 --- a/packages/googleapis_storage/lib/src/utils/auth_extension.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; - -extension AuthExtension on googleapis_auth.AuthClient { - Future get getServiceAccountEmail async => - serviceAccountCredentials?.email ?? - googleapis_auth.IAMSigner(this).getServiceAccountEmail(); -} diff --git a/pubspec.yaml b/pubspec.yaml index ccb41a6a..be1fc9ce 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,10 @@ dev_dependencies: melos: ^7.3.0 test: ^1.26.3 +dependency_overrides: + googleapis_auth: + path: ../googleapis.dart/googleapis_auth + workspace: - packages/dart_firebase_admin - packages/googleapis_firestore From aa349ce1365dcfff2cfb130d7c581941993c4f3d Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 17 Feb 2026 16:39:04 -0800 Subject: [PATCH 2/6] OVERRIDES for in-flight PR Will replace when https://github.com/google/googleapis.dart/pull/717 lands --- packages/dart_firebase_admin/pubspec.yaml | 2 +- packages/googleapis_firestore/pubspec.yaml | 2 +- packages/googleapis_storage/pubspec.yaml | 2 +- pubspec.yaml | 5 ++++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/dart_firebase_admin/pubspec.yaml b/packages/dart_firebase_admin/pubspec.yaml index 5cccb66d..2a3985a2 100644 --- a/packages/dart_firebase_admin/pubspec.yaml +++ b/packages/dart_firebase_admin/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: collection: ^1.18.0 dart_jsonwebtoken: ^3.0.0 equatable: ^2.0.7 - google_cloud: ^0.2.1-beta.1 + google_cloud: ^0.3.0 googleapis: ^15.0.0 googleapis_auth: ^2.1.0-beta.1 googleapis_beta: ^9.0.0 diff --git a/packages/googleapis_firestore/pubspec.yaml b/packages/googleapis_firestore/pubspec.yaml index 519e4fb4..ffc9a077 100644 --- a/packages/googleapis_firestore/pubspec.yaml +++ b/packages/googleapis_firestore/pubspec.yaml @@ -9,7 +9,7 @@ environment: dependencies: collection: ^1.18.0 - google_cloud: ^0.2.1-beta.1 + google_cloud: ^0.3.0 googleapis: ^15.0.0 googleapis_auth: ^2.0.0 http: ^1.0.0 diff --git a/packages/googleapis_storage/pubspec.yaml b/packages/googleapis_storage/pubspec.yaml index 8d84b3d8..623738fc 100644 --- a/packages/googleapis_storage/pubspec.yaml +++ b/packages/googleapis_storage/pubspec.yaml @@ -10,7 +10,7 @@ environment: dependencies: googleapis_auth: ^2.0.0 googleapis: ^15.0.0 - google_cloud: ^0.2.0 + google_cloud: ^0.3.0 http: ^1.6.0 meta: ^1.17.0 mime: ^2.0.0 diff --git a/pubspec.yaml b/pubspec.yaml index be1fc9ce..6595653b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,7 +10,10 @@ dev_dependencies: dependency_overrides: googleapis_auth: - path: ../googleapis.dart/googleapis_auth + git: + url: https://github.com/google/googleapis.dart.git + ref: drop_signer_class + path: googleapis_auth workspace: - packages/dart_firebase_admin From 9b2e07f1b3ed0548c2a1d2ee664c5f6d9bd9526e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 17 Feb 2026 16:48:25 -0800 Subject: [PATCH 3/6] test: fix functions test mock for metadata server email resolution --- .../test/functions/functions_test.dart | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/dart_firebase_admin/test/functions/functions_test.dart b/packages/dart_firebase_admin/test/functions/functions_test.dart index 2282d83d..0aea0059 100644 --- a/packages/dart_firebase_admin/test/functions/functions_test.dart +++ b/packages/dart_firebase_admin/test/functions/functions_test.dart @@ -26,6 +26,7 @@ class FakeBaseRequest extends Fake implements BaseRequest {} /// Creates a mock HTTP client that handles OAuth token requests and /// optionally Cloud Tasks API requests. MockClient createMockHttpClient({ + String? email, String? idToken, Response Function(Request)? apiHandler, }) { @@ -60,6 +61,15 @@ MockClient createMockHttpClient({ ); } + // Handle Metadata Server requests for service account email + if (request.url.host == 'metadata.google.internal' && + request.url.path.contains('/service-accounts/default/email')) { + if (email != null) { + return Response(email, 200, headers: {'Metadata-Flavor': 'Google'}); + } + return Response('Not Found', 404); + } + // Default response return Response('{}', 200); }); @@ -75,6 +85,7 @@ Future createTestAuthClient({ Response Function(Request)? apiHandler, }) async { final baseClient = createMockHttpClient( + email: email, idToken: idToken, apiHandler: apiHandler, ); From 859fefdf9f9c89c25d79fbe36b36816ac68e3f9b Mon Sep 17 00:00:00 2001 From: demolaf Date: Wed, 18 Feb 2026 18:13:33 +0100 Subject: [PATCH 4/6] feat: add extension methods for FirebaseApp to handle service account email and signing --- .../lib/src/app/app_registry.dart | 19 +++++++++++---- .../lib/src/app_check/token_generator.dart | 10 +++----- .../dart_firebase_admin/lib/src/auth.dart | 1 + .../lib/src/auth/token_generator.dart | 9 ++------ .../lib/src/functions/functions.dart | 1 + .../functions/functions_request_handler.dart | 2 +- .../lib/src/utils/app_extension.dart | 23 +++++++++++++++++++ .../test/auth/auth_test.dart | 9 +++++++- .../dart_firebase_admin/test/helpers.dart | 7 +++++- packages/googleapis_storage/lib/src/file.dart | 8 ++++++- .../googleapis_storage/lib/src/signer.dart | 12 ++++++++-- 11 files changed, 77 insertions(+), 24 deletions(-) create mode 100644 packages/dart_firebase_admin/lib/src/utils/app_extension.dart diff --git a/packages/dart_firebase_admin/lib/src/app/app_registry.dart b/packages/dart_firebase_admin/lib/src/app/app_registry.dart index c89e7253..3db9cd43 100644 --- a/packages/dart_firebase_admin/lib/src/app/app_registry.dart +++ b/packages/dart_firebase_admin/lib/src/app/app_registry.dart @@ -100,9 +100,7 @@ class AppRegistry { final config = env['FIREBASE_CONFIG']; if (config == null || config.isEmpty) { - return AppOptions( - credential: Credential.fromApplicationDefaultCredentials(), - ); + return AppOptions(credential: _credentialFromEnv(env)); } try { @@ -118,7 +116,7 @@ class AppRegistry { final json = jsonDecode(contents) as Map; return AppOptions( - credential: Credential.fromApplicationDefaultCredentials(), + credential: _credentialFromEnv(env), projectId: json['projectId'] as String?, databaseURL: json['databaseURL'] as String?, storageBucket: json['storageBucket'] as String?, @@ -186,4 +184,17 @@ class AppRegistry { ); } } + + Credential _credentialFromEnv(Map env) { + final googleCredPath = env['GOOGLE_APPLICATION_CREDENTIALS']; + if (googleCredPath != null) { + try { + return Credential.fromServiceAccount(File(googleCredPath)); + } catch (_) { + // File missing, not a service account JSON, or missing project_id – + // fall through to ADC. + } + } + return Credential.fromApplicationDefaultCredentials(); + } } diff --git a/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart b/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart index 2e1e03d3..dcb2906d 100644 --- a/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart +++ b/packages/dart_firebase_admin/lib/src/app_check/token_generator.dart @@ -4,6 +4,7 @@ import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; import 'package:meta/meta.dart'; import '../../dart_firebase_admin.dart'; +import '../utils/app_extension.dart'; import 'app_check.dart'; import 'app_check_api.dart'; @@ -31,8 +32,7 @@ class AppCheckTokenGenerator { AppCheckTokenOptions? options, ]) async { try { - final authClient = await app.client; - final account = await authClient.getServiceAccountEmail(); + final account = await app.serviceAccountEmail; final header = {'alg': 'RS256', 'typ': 'JWT'}; final iat = (DateTime.now().millisecondsSinceEpoch / 1000).floor(); @@ -47,11 +47,7 @@ class AppCheckTokenGenerator { final token = '${_encodeSegment(header)}.${_encodeSegment(body)}'; - final signature = await authClient.sign( - utf8.encode(token), - serviceAccountCredentials: - app.options.credential?.serviceAccountCredentials, - ); + final signature = await app.sign(utf8.encode(token)); return '$token.$signature'; } on googleapis_auth.ServerRequestFailedException catch (err) { diff --git a/packages/dart_firebase_admin/lib/src/auth.dart b/packages/dart_firebase_admin/lib/src/auth.dart index 83502ca4..5b25abcb 100644 --- a/packages/dart_firebase_admin/lib/src/auth.dart +++ b/packages/dart_firebase_admin/lib/src/auth.dart @@ -14,6 +14,7 @@ import 'package:meta/meta.dart'; import 'app.dart'; import 'object_utils.dart'; +import 'utils/app_extension.dart'; import 'utils/jwt.dart'; import 'utils/utils.dart'; import 'utils/validator.dart'; diff --git a/packages/dart_firebase_admin/lib/src/auth/token_generator.dart b/packages/dart_firebase_admin/lib/src/auth/token_generator.dart index eeedadab..620a8699 100644 --- a/packages/dart_firebase_admin/lib/src/auth/token_generator.dart +++ b/packages/dart_firebase_admin/lib/src/auth/token_generator.dart @@ -70,8 +70,7 @@ class _FirebaseTokenGenerator { } try { - final authClient = await _app.client; - final account = await authClient.getServiceAccountEmail(); + final account = await _app.serviceAccountEmail; final header = {'alg': 'RS256', 'typ': 'JWT'}; final iat = DateTime.now().millisecondsSinceEpoch ~/ 1000; @@ -87,11 +86,7 @@ class _FirebaseTokenGenerator { }; final token = '${_encodeSegment(header)}.${_encodeSegment(body)}'; - final signature = await authClient.sign( - utf8.encode(token), - serviceAccountCredentials: - _app.options.credential?.serviceAccountCredentials, - ); + final signature = await _app.sign(utf8.encode(token)); return '$token.$signature'; } on googleapis_auth.ServerRequestFailedException catch (err, stack) { diff --git a/packages/dart_firebase_admin/lib/src/functions/functions.dart b/packages/dart_firebase_admin/lib/src/functions/functions.dart index 1a5463f0..0e0c6f40 100644 --- a/packages/dart_firebase_admin/lib/src/functions/functions.dart +++ b/packages/dart_firebase_admin/lib/src/functions/functions.dart @@ -5,6 +5,7 @@ import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; import 'package:meta/meta.dart'; import '../app.dart'; +import '../utils/app_extension.dart'; import '../utils/validator.dart'; part 'functions_api.dart'; diff --git a/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart b/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart index 1e97bf5e..d5e669a7 100644 --- a/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart +++ b/packages/dart_firebase_admin/lib/src/functions/functions_request_handler.dart @@ -274,7 +274,7 @@ class FunctionsRequestHandler { // Default: Use OIDC token with service account email. // Try to get service account email from credential first, then from metadata service. - final serviceAccountEmail = await authClient.getServiceAccountEmail(); + final serviceAccountEmail = await _httpClient.app.serviceAccountEmail; if (serviceAccountEmail.isEmpty) { throw FirebaseFunctionsAdminException( diff --git a/packages/dart_firebase_admin/lib/src/utils/app_extension.dart b/packages/dart_firebase_admin/lib/src/utils/app_extension.dart new file mode 100644 index 00000000..05b5da06 --- /dev/null +++ b/packages/dart_firebase_admin/lib/src/utils/app_extension.dart @@ -0,0 +1,23 @@ +import 'package:googleapis_auth/auth_io.dart'; + +import '../app.dart'; + +extension AppExtension on FirebaseApp { + Future get serviceAccountEmail async => + options.credential?.serviceAccountCredentials?.email ?? + (await client).getServiceAccountEmail(); + + /// Signs the given data using the IAM Credentials API or local credentials. + /// + /// Returns a base64-encoded signature string. In emulator mode, returns an + /// empty string to produce unsigned tokens. + Future sign(List data, {String? endpoint}) async => + Environment.isAuthEmulatorEnabled() + ? '' + : (await client).sign( + data, + serviceAccountCredentials: + options.credential?.serviceAccountCredentials, + endpoint: endpoint, + ); +} diff --git a/packages/dart_firebase_admin/test/auth/auth_test.dart b/packages/dart_firebase_admin/test/auth/auth_test.dart index c77da1d6..bccb4f7c 100644 --- a/packages/dart_firebase_admin/test/auth/auth_test.dart +++ b/packages/dart_firebase_admin/test/auth/auth_test.dart @@ -9,12 +9,19 @@ import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; import '../helpers.dart'; import '../mock.dart'; +import '../mock_service_account.dart'; void main() { late Auth auth; setUp(() { - final sdk = createApp(); + final sdk = createApp( + credential: Credential.fromServiceAccountParams( + privateKey: mockPrivateKey, + email: mockClientEmail, + projectId: projectId, + ), + ); auth = Auth.internal(sdk); }); diff --git a/packages/dart_firebase_admin/test/helpers.dart b/packages/dart_firebase_admin/test/helpers.dart index da58f7a8..aea1b6d5 100644 --- a/packages/dart_firebase_admin/test/helpers.dart +++ b/packages/dart_firebase_admin/test/helpers.dart @@ -39,10 +39,15 @@ FirebaseApp createApp({ FutureOr Function()? tearDown, googleapis_auth.AuthClient? client, String? name, + Credential? credential, }) { final app = FirebaseApp.initializeApp( name: name, - options: AppOptions(projectId: projectId, httpClient: client), + options: AppOptions( + projectId: projectId, + httpClient: client, + credential: credential, + ), ); addTearDown(() async { diff --git a/packages/googleapis_storage/lib/src/file.dart b/packages/googleapis_storage/lib/src/file.dart index 34542512..f5d3b8e9 100644 --- a/packages/googleapis_storage/lib/src/file.dart +++ b/packages/googleapis_storage/lib/src/file.dart @@ -1003,6 +1003,8 @@ class BucketFile extends ServiceObject final authClient = await storage.authClient; final signature = await authClient.sign( policyStringBytes, + serviceAccountCredentials: + bucket.storage.options.credential?.serviceAccountCredentials, endpoint: options.signingEndpoint?.toString(), ); @@ -1056,7 +1058,9 @@ class BucketFile extends ServiceObject // Get auth client and credentials final authClient = await storage.authClient; - final clientEmail = await authClient.getServiceAccountEmail(); + final clientEmail = + bucket.storage.options.credential?.serviceAccountCredentials?.email ?? + await authClient.getServiceAccountEmail(); // Build credential string final todayISO = _formatDateStamp(now); @@ -1098,6 +1102,8 @@ class BucketFile extends ServiceObject try { final signature = await authClient.sign( policyStringBytes, + serviceAccountCredentials: + bucket.storage.options.credential?.serviceAccountCredentials, endpoint: options.signingEndpoint?.toString(), ); diff --git a/packages/googleapis_storage/lib/src/signer.dart b/packages/googleapis_storage/lib/src/signer.dart index 9090bedb..b48a376b 100644 --- a/packages/googleapis_storage/lib/src/signer.dart +++ b/packages/googleapis_storage/lib/src/signer.dart @@ -135,10 +135,14 @@ class URLSigner { final authClient = await bucket.storage.authClient; final signature = await authClient.sign( utf8.encode(blobToSign), + serviceAccountCredentials: + bucket.storage.options.credential?.serviceAccountCredentials, endpoint: config.signedConfig.signingEndpoint?.toString(), ); - final clientEmail = await authClient.getServiceAccountEmail(); + final clientEmail = + bucket.storage.options.credential?.serviceAccountCredentials?.email ?? + await authClient.getServiceAccountEmail(); return { 'GoogleAccessId': clientEmail, @@ -202,7 +206,9 @@ class URLSigner { final authClient = await bucket.storage.authClient; - final clientEmail = await authClient.getServiceAccountEmail(); + final clientEmail = + bucket.storage.options.credential?.serviceAccountCredentials?.email ?? + await authClient.getServiceAccountEmail(); final credentialString = '$clientEmail/$credentialScope'; final dateISO = _formatAsUTCISO(config.accessibleAt, includeTime: true); @@ -239,6 +245,8 @@ class URLSigner { final signature = await authClient.sign( utf8.encode(blobToSign), + serviceAccountCredentials: + bucket.storage.options.credential?.serviceAccountCredentials, endpoint: config.signedConfig.signingEndpoint?.toString(), ); From 514a79781bb4cc74304115b8bdbdd4896b8765fe Mon Sep 17 00:00:00 2001 From: demolaf Date: Wed, 18 Feb 2026 19:49:42 +0100 Subject: [PATCH 5/6] chore: bump dart_firebase_admin to 0.5.0 and googleapis_auth dependency to 2.1.0 --- packages/dart_firebase_admin/lib/version.g.dart | 2 +- packages/dart_firebase_admin/pubspec.yaml | 4 ++-- packages/googleapis_firestore/pubspec.yaml | 2 +- packages/googleapis_storage/pubspec.yaml | 2 +- pubspec.yaml | 7 ------- 5 files changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/dart_firebase_admin/lib/version.g.dart b/packages/dart_firebase_admin/lib/version.g.dart index f70f7cfb..59c5d2ac 100644 --- a/packages/dart_firebase_admin/lib/version.g.dart +++ b/packages/dart_firebase_admin/lib/version.g.dart @@ -2,4 +2,4 @@ // This file is generated by gen-version.sh /// The current version of the package. -const String packageVersion = '1.0.0-beta.1'; +const String packageVersion = '0.5.0'; diff --git a/packages/dart_firebase_admin/pubspec.yaml b/packages/dart_firebase_admin/pubspec.yaml index 2a3985a2..db1a9e94 100644 --- a/packages/dart_firebase_admin/pubspec.yaml +++ b/packages/dart_firebase_admin/pubspec.yaml @@ -1,7 +1,7 @@ name: dart_firebase_admin description: A Firebase Admin SDK implementation for Dart. resolution: workspace -version: 1.0.0-beta.1 +version: 0.5.0 homepage: "https://github.com/invertase/dart_firebase_admin" repository: "https://github.com/invertase/dart_firebase_admin" publish_to: none @@ -16,7 +16,7 @@ dependencies: equatable: ^2.0.7 google_cloud: ^0.3.0 googleapis: ^15.0.0 - googleapis_auth: ^2.1.0-beta.1 + googleapis_auth: ^2.1.0 googleapis_beta: ^9.0.0 googleapis_firestore: ^0.1.0 googleapis_storage: ^0.1.0 diff --git a/packages/googleapis_firestore/pubspec.yaml b/packages/googleapis_firestore/pubspec.yaml index ffc9a077..eb1e03de 100644 --- a/packages/googleapis_firestore/pubspec.yaml +++ b/packages/googleapis_firestore/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: collection: ^1.18.0 google_cloud: ^0.3.0 googleapis: ^15.0.0 - googleapis_auth: ^2.0.0 + googleapis_auth: ^2.1.0 http: ^1.0.0 intl: ^0.20.0 meta: ^1.9.1 diff --git a/packages/googleapis_storage/pubspec.yaml b/packages/googleapis_storage/pubspec.yaml index 623738fc..3459e0d6 100644 --- a/packages/googleapis_storage/pubspec.yaml +++ b/packages/googleapis_storage/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: ">=3.9.0 <4.0.0" dependencies: - googleapis_auth: ^2.0.0 + googleapis_auth: ^2.1.0 googleapis: ^15.0.0 google_cloud: ^0.3.0 http: ^1.6.0 diff --git a/pubspec.yaml b/pubspec.yaml index 6595653b..ccb41a6a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,13 +8,6 @@ dev_dependencies: melos: ^7.3.0 test: ^1.26.3 -dependency_overrides: - googleapis_auth: - git: - url: https://github.com/google/googleapis.dart.git - ref: drop_signer_class - path: googleapis_auth - workspace: - packages/dart_firebase_admin - packages/googleapis_firestore From e36f0db2f91e2e10ea6c77f8b8f66f26df21f269 Mon Sep 17 00:00:00 2001 From: demolaf Date: Wed, 18 Feb 2026 19:56:39 +0100 Subject: [PATCH 6/6] chore: update changelog --- packages/dart_firebase_admin/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dart_firebase_admin/CHANGELOG.md b/packages/dart_firebase_admin/CHANGELOG.md index 22838e09..58875912 100644 --- a/packages/dart_firebase_admin/CHANGELOG.md +++ b/packages/dart_firebase_admin/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.0.0-beta.1 - 2026-02-12 +## 0.5.0 - 2026-02-12 - Major changes