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 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/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/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_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/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(), ); 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