Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ jobs:
working-directory: .

- name: Run WIF auth test
run: dart test test/integration/app/firebase_app_prod_test.dart --concurrency=1 --tags wif
run: dart test test/integration/app/firebase_app_prod_test.dart --concurrency=1 -P wif

publish:
name: Publish verification
Expand Down
16 changes: 16 additions & 0 deletions packages/firebase_admin_sdk/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
exclude_tags: firebase-emulator || prod || wif

presets:
ci:
exclude_tags: prod || wif
firebase-emulator:
include_tags: firebase-emulator
prod:
include_tags: prod
wif:
include_tags: wif

tags:
firebase-emulator:
description: >
Tests that require Firebase emulators (Auth, Firestore, Storage, Functions).
Run with: firebase emulators:exec "dart test -P firebase-emulator"
prod:
description: >
Tests that hit real Firebase production APIs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class _ActionCodeSettingsBuilder {
_apn = actionCodeSettings.android?.packageName,
_amv = actionCodeSettings.android?.minimumVersion,
_installApp = actionCodeSettings.android?.installApp ?? false {
if (Uri.tryParse(actionCodeSettings.url) == null) {
if (Uri.tryParse(actionCodeSettings.url)?.isAbsolute != true) {
throw FirebaseAuthAdminException(AuthClientErrorCode.invalidContinueUri);
}

Expand Down
44 changes: 24 additions & 20 deletions packages/firebase_admin_sdk/lib/src/auth/auth_request_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@ abstract class _AbstractAuthRequestHandler {
ActionCodeSettings? actionCodeSettings, [
String? newEmail,
]) async {
assertIsEmail(email);

if (requestType == 'VERIFY_AND_CHANGE_EMAIL') {
if (newEmail == null || newEmail.isEmpty) {
throw FirebaseAuthAdminException(
AuthClientErrorCode.invalidArgument,
"`newEmail` is required when `requestType` === 'VERIFY_AND_CHANGE_EMAIL'",
);
}
assertIsEmail(newEmail);
}

final request = auth1.GoogleCloudIdentitytoolkitV1GetOobCodeRequest(
requestType: requestType,
email: email,
Expand All @@ -84,13 +96,6 @@ abstract class _AbstractAuthRequestHandler {
builder.buildRequest(request);
}

if (requestType == 'VERIFY_AND_CHANGE_EMAIL' && newEmail == null) {
throw FirebaseAuthAdminException(
AuthClientErrorCode.invalidArgument,
"`newEmail` is required when `requestType` === 'VERIFY_AND_CHANGE_EMAIL'",
);
}

final response = await _httpClient.getOobCode(request);
return response.oobLink!;
}
Expand Down Expand Up @@ -325,28 +330,27 @@ abstract class _AbstractAuthRequestHandler {
/// session management (set as a server side session cookie with custom cookie policy).
/// The session cookie JWT will have the same payload claims as the provided ID token.
Future<String> createSessionCookie(String idToken, {required int expiresIn}) {
if (idToken.isEmpty) {
throw FirebaseAuthAdminException(AuthClientErrorCode.invalidIdToken);
}

// Convert to seconds (use integer division to avoid decimal).
final validDuration = expiresIn ~/ 1000;

if (validDuration < _minSessionCookieDurationSecs ||
validDuration > _maxSessionCookieDurationSecs) {
throw FirebaseAuthAdminException(
AuthClientErrorCode.invalidSessionCookieDuration,
);
}

final request =
auth1.GoogleCloudIdentitytoolkitV1CreateSessionCookieRequest(
idToken: idToken,
validDuration: validDuration.toString(),
);

return _httpClient.v1((api, projectId) async {
// Validate the ID token is a non-empty string.
if (idToken.isEmpty) {
throw FirebaseAuthAdminException(AuthClientErrorCode.invalidIdToken);
}

// Validate the custom session cookie duration.
if (validDuration < _minSessionCookieDurationSecs ||
validDuration > _maxSessionCookieDurationSecs) {
throw FirebaseAuthAdminException(
AuthClientErrorCode.invalidSessionCookieDuration,
);
}

final response = await api.projects.createSessionCookie(
request,
projectId,
Expand Down
29 changes: 19 additions & 10 deletions packages/firebase_admin_sdk/lib/src/utils/app_extension.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import 'dart:convert';

import 'package:googleapis_auth/auth_io.dart';

import '../app.dart';
Expand All @@ -25,14 +27,21 @@ extension AppExtension on FirebaseApp {
///
/// Returns a base64-encoded signature string. In emulator mode, returns an
/// empty string to produce unsigned tokens.
Future<String> sign(List<int> data, {String? endpoint}) async =>
Environment.isAuthEmulatorEnabled()
? ''
: (await client).sign(
data,
serviceAccountCredentials:
options.credential?.serviceAccountCredentials,
serviceAccountEmail: options.credential?.serviceAccountId,
endpoint: endpoint,
);
Future<String> sign(List<int> data, {String? endpoint}) async {
if (Environment.isAuthEmulatorEnabled()) return '';

// When a service account private key is available, sign locally without
// making an OAuth network call. This avoids a round-trip to the token
// endpoint and works in unit tests with mock credentials.
final creds = options.credential?.serviceAccountCredentials;
if (creds != null) {
return base64Encode(creds.sign(data));
}

return (await client).sign(
data,
serviceAccountEmail: options.credential?.serviceAccountId,
endpoint: endpoint,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ void main() {
? false
: 'Skipping Firestore emulator lifecycle tests. '
'Set FIRESTORE_EMULATOR_HOST to run these tests.',
tags: 'firebase-emulator',
);

group(
Expand Down Expand Up @@ -142,6 +143,7 @@ void main() {
? false
: 'Skipping Auth emulator lifecycle tests. '
'Set FIREBASE_AUTH_EMULATOR_HOST to run these tests.',
tags: 'firebase-emulator',
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
// To run these tests:
// FIREBASE_AUTH_EMULATOR_HOST=localhost:9099 dart test test/auth/integration_test.dart

@Tags(['firebase-emulator'])
library;

import 'dart:convert';
import 'dart:io';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
// Run with:
// FIREBASE_AUTH_EMULATOR_HOST=localhost:9099 dart test test/auth/project_config_integration_test.dart

@Tags(['firebase-emulator'])
library;

import 'package:firebase_admin_sdk/auth.dart';
import 'package:test/test.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
// Run with:
// FIREBASE_AUTH_EMULATOR_HOST=localhost:9099 dart test test/auth/tenant_integration_test.dart

@Tags(['firebase-emulator'])
library;

import 'package:firebase_admin_sdk/auth.dart';
import 'package:firebase_admin_sdk/src/app.dart';
import 'package:test/test.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

@Tags(['firebase-emulator'])
library;

import 'package:firebase_admin_sdk/src/app.dart';
import 'package:google_cloud_firestore/google_cloud_firestore.dart' as gfs;
import 'package:test/test.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

@Tags(['firebase-emulator'])
library;

import 'package:firebase_admin_sdk/functions.dart';
import 'package:test/test.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

@Tags(['firebase-emulator'])
library;

import 'dart:typed_data';

import 'package:firebase_admin_sdk/src/app.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:google_cloud_firestore/google_cloud_firestore.dart';
import 'package:google_cloud_firestore/src/firestore.dart' show FieldMask;
import 'package:test/test.dart';
import '../fixtures/helpers.dart' as helpers;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
// limitations under the License.

import 'package:google_cloud_firestore/google_cloud_firestore.dart';
import 'package:google_cloud_firestore/src/firestore.dart' show FieldMask;
import 'package:google_cloud_firestore/src/firestore_http_client.dart';
import 'package:google_cloud_firestore_v1/firestore.dart' as firestore_v1;
import 'package:google_cloud_protobuf/protobuf.dart' as protobuf_v1;
Expand Down
2 changes: 1 addition & 1 deletion scripts/coverage.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ cd ../../..
dart pub global activate coverage

# Exclude prod/wif tests unless a credential is available.
TEST_TAGS="--exclude-tags prod,wif"
TEST_TAGS="-P ci"
if [ -n "${GOOGLE_APPLICATION_CREDENTIALS:-}" ]; then
TEST_TAGS=""
fi
Expand Down
Loading