Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(auth, android): setSettings now possible for android #6367

Merged
merged 4 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,7 @@ public class Constants {
public static final String HOST = "host";
public static final String PORT = "port";
public static final String NAME = "name";
public static final String APP_VERIFICATION_DISABLED_FOR_TESTING =
"appVerificationDisabledForTesting";
public static final String FORCE_RECAPTCHA_FLOW = "forceRecaptchaFlow";
}
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,35 @@ private Task<Map<String, Object>> setLanguageCode(Map<String, Object> arguments)
});
}

// Settings are a no-op on Android
private Task<Void> setSettings() {
return Tasks.call(cachedThreadPool, () -> null);
private Task<Void> setSettings(Map<String, Object> arguments) {
return Tasks.call(
cachedThreadPool,
() -> {
FirebaseAuth firebaseAuth = getAuth(arguments);
Boolean appVerificationDisabledForTesting =
(Boolean) arguments.get(Constants.APP_VERIFICATION_DISABLED_FOR_TESTING);
Boolean forceRecaptchaFlow = (Boolean) arguments.get(Constants.FORCE_RECAPTCHA_FLOW);
String phoneNumber = (String) arguments.get(Constants.PHONE_NUMBER);
String smsCode = (String) arguments.get(Constants.SMS_CODE);

if (appVerificationDisabledForTesting != null) {
firebaseAuth
.getFirebaseAuthSettings()
.setAppVerificationDisabledForTesting(appVerificationDisabledForTesting);
}

if (forceRecaptchaFlow != null) {
firebaseAuth.getFirebaseAuthSettings().forceRecaptchaFlowForTesting(forceRecaptchaFlow);
}

if (phoneNumber != null && smsCode != null) {
firebaseAuth
.getFirebaseAuthSettings()
.setAutoRetrievedSmsCodeForPhoneNumber(phoneNumber, smsCode);
}

return null;
});
}

private Task<Map<String, Object>> signInAnonymously(Map<String, Object> arguments) {
Expand Down Expand Up @@ -1035,7 +1061,7 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
methodCallTask = setLanguageCode(call.arguments());
break;
case "Auth#setSettings":
methodCallTask = setSettings();
methodCallTask = setSettings(call.arguments());
break;
case "Auth#signInAnonymously":
methodCallTask = signInAnonymously(call.arguments());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,24 @@ void runInstanceTests() {
}, skip: kIsWeb || defaultTargetPlatform != TargetPlatform.android);
}, skip: defaultTargetPlatform == TargetPlatform.macOS || kIsWeb);

group('setSettings()', () {
test(
'throws argument error if phoneNumber & smsCode have not been set simultaneously',
() async {
String message =
"The [smsCode] and the [phoneNumber] must both be either 'null' or a 'String''.";
await expectLater(
FirebaseAuth.instance.setSettings(phoneNumber: '123456'),
throwsA(isA<ArgumentError>()
.having((e) => e.message, 'message', contains(message))));

await expectLater(
FirebaseAuth.instance.setSettings(smsCode: '123456'),
throwsA(isA<ArgumentError>()
.having((e) => e.message, 'message', contains(message))));
}, skip: kIsWeb || defaultTargetPlatform != TargetPlatform.android);
});

group('tenantId', () {
test('User associated with the tenantId correctly', () async {
// tenantId created in the GCP console
Expand Down
11 changes: 8 additions & 3 deletions packages/firebase_auth/firebase_auth/lib/src/firebase_auth.dart
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,16 @@ class FirebaseAuth extends FirebasePluginPlatform {
Future<void> setSettings({
bool? appVerificationDisabledForTesting,
String? userAccessGroup,
String? phoneNumber,
String? smsCode,
bool? forceRecaptchaFlow,
}) {
return _delegate.setSettings(
appVerificationDisabledForTesting: appVerificationDisabledForTesting,
userAccessGroup: userAccessGroup,
);
appVerificationDisabledForTesting: appVerificationDisabledForTesting,
userAccessGroup: userAccessGroup,
phoneNumber: phoneNumber,
smsCode: smsCode,
forceRecaptchaFlow: forceRecaptchaFlow);
}

/// Changes the current type of persistence on the current Auth instance for
Expand Down
33 changes: 31 additions & 2 deletions packages/firebase_auth/firebase_auth/test/firebase_auth_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -416,12 +416,35 @@ void main() {
// Necessary as we otherwise get a "null is not a Future<void>" error
when(mockAuthPlatform.setSettings(
appVerificationDisabledForTesting: any,
phoneNumber: any,
smsCode: any,
forceRecaptchaFlow: any,
userAccessGroup: any,
)).thenAnswer((i) async {});

await auth.setSettings(appVerificationDisabledForTesting: true);
String phoneNumber = '123456';
String smsCode = '1234';
bool forceRecaptchaFlow = true;
bool appVerificationDisabledForTesting = true;
String userAccessGroup = 'group-id';

await auth.setSettings(
appVerificationDisabledForTesting: appVerificationDisabledForTesting,
phoneNumber: phoneNumber,
smsCode: smsCode,
forceRecaptchaFlow: forceRecaptchaFlow,
userAccessGroup: userAccessGroup,
);

verify(
mockAuthPlatform.setSettings(appVerificationDisabledForTesting: true),
mockAuthPlatform.setSettings(
appVerificationDisabledForTesting:
appVerificationDisabledForTesting,
phoneNumber: phoneNumber,
smsCode: smsCode,
forceRecaptchaFlow: forceRecaptchaFlow,
userAccessGroup: userAccessGroup,
),
);
});
});
Expand Down Expand Up @@ -916,11 +939,17 @@ class MockFirebaseAuth extends Mock
Future<void> setSettings({
bool? appVerificationDisabledForTesting,
String? userAccessGroup,
String? phoneNumber,
String? smsCode,
bool? forceRecaptchaFlow,
}) {
return super.noSuchMethod(
Invocation.method(#setSettings, [
appVerificationDisabledForTesting,
userAccessGroup,
phoneNumber,
smsCode,
forceRecaptchaFlow,
]),
returnValue: neverEndingFuture<void>(),
returnValueForMissingStub: neverEndingFuture<void>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// found in the LICENSE file.

import 'dart:async';
import 'dart:io' show Platform;

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -364,15 +365,39 @@ class MethodChannelFirebaseAuth extends FirebaseAuthPlatform {
Future<void> setSettings({
bool? appVerificationDisabledForTesting,
String? userAccessGroup,
String? phoneNumber,
String? smsCode,
bool? forceRecaptchaFlow,
}) async {
if (phoneNumber != null && smsCode == null ||
phoneNumber == null && smsCode != null) {
throw ArgumentError(
"The [smsCode] and the [phoneNumber] must both be either 'null' or a 'String''.",
);
}
// argument for every platform
var arguments = <String, dynamic>{
'appVerificationDisabledForTesting': appVerificationDisabledForTesting,
};

if (Platform.isIOS || Platform.isMacOS) {
arguments['userAccessGroup'] = userAccessGroup;
}

if (Platform.isAndroid) {
if (phoneNumber != null && smsCode != null) {
arguments['phoneNumber'] = phoneNumber;
arguments['smsCode'] = smsCode;
}

if (forceRecaptchaFlow != null) {
arguments['forceRecaptchaFlow'] = forceRecaptchaFlow;
}
}

try {
await channel.invokeMethod(
'Auth#setSettings',
_withChannelDefaults({
'appVerificationDisabledForTesting':
appVerificationDisabledForTesting,
'userAccessGroup': userAccessGroup,
}));
'Auth#setSettings', _withChannelDefaults(arguments));
} catch (e) {
throw convertPlatformException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ abstract class FirebaseAuthPlatform extends PlatformInterface {

/// Updates the current instance with the provided settings.
///
/// [appVerificationDisabledForTesting] This setting only applies to iOS and
/// [appVerificationDisabledForTesting] This setting applies to android, iOS and
/// web platforms. When set to `true`, this property disables app
/// verification for the purpose of testing phone authentication. For this
/// property to take effect, it needs to be set before handling a reCAPTCHA
Expand All @@ -337,17 +337,32 @@ abstract class FirebaseAuthPlatform extends PlatformInterface {
///
/// The default value is `false` (app verification is enabled).
///
/// [userAccessGroup] This setting only applies to iOS and MacOS platforms.
/// When set, it allows you to share authentication state between
/// applications. Set the property to your team group ID or set to `null`
/// to remove sharing capabilities.
/// [forceRecaptchaFlow] This setting applies to android only. When set to 'true',
/// it forces the application verification to use the web reCAPTCHA flow for Phone Authentication.
/// Once this has been called, every call to PhoneAuthProvider#verifyPhoneNumber() will skip the SafetyNet verification flow and use the reCAPTCHA flow instead.
/// Calling this method a second time will overwrite the previously passed parameter.
///
/// [phoneNumber] & [smsCode] These settings apply to android only. The phone number and SMS code here must have been configured in the Firebase Console (Authentication > Sign In Method > Phone).
/// Once this has been called, every call to PhoneAuthProvider#verifyPhoneNumber() with the same phone number as the one that is configured here will have onVerificationCompleted() triggered as the callback.
/// Calling this method a second time will overwrite the previously passed parameters. Only one number can be configured at a given time.
/// Calling this method with either parameter set to null removes this functionality until valid parameters are passed.
/// Verifying a phone number other than the one configured here will trigger normal behaviour. If the phone number is configured as a test phone number in the console, the regular testing flow occurs. Otherwise, normal phone number verification will take place.
/// When this is set and PhoneAuthProvider#verifyPhoneNumber() is called with a matching phone number, PhoneAuthProvider.OnVerificationStateChangedCallbacks.onCodeAutoRetrievalTimeOut(String) will never be called.
///
/// [userAccessGroup] This setting only applies to iOS and MacOS platforms.
/// When set, it allows you to share authentication state between
/// applications. Set the property to your team group ID or set to `null`
/// to remove sharing capabilities.
///
/// Key Sharing capabilities must be enabled for your app via XCode (Project
/// settings > Capabilities). To learn more, visit the
/// [Apple documentation](https://developer.apple.com/documentation/security/keychain_services/keychain_items/sharing_access_to_keychain_items_among_a_collection_of_apps).
Future<void> setSettings({
bool? appVerificationDisabledForTesting,
String? userAccessGroup,
String? phoneNumber,
String? smsCode,
bool? forceRecaptchaFlow,
}) {
throw UnimplementedError('setSettings() is not implemented');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:firebase_auth_platform_interface/firebase_auth_platform_interface.dart';
import 'package:firebase_auth_platform_interface/src/method_channel/method_channel_firebase_auth.dart';
Expand Down Expand Up @@ -737,20 +738,47 @@ void main() {
group('setSettings()', () {
const bool isDisabled = true;
test('invokes native method with correct args', () async {
await auth.setSettings(appVerificationDisabledForTesting: isDisabled);
String groupId = 'group-id';
String phoneNumber = '555-5555';
String smsCode = '123456';
bool forceRecaptchaFlow = true;

await auth.setSettings(
appVerificationDisabledForTesting: isDisabled,
userAccessGroup: groupId,
phoneNumber: phoneNumber,
smsCode: smsCode,
forceRecaptchaFlow: forceRecaptchaFlow,
);

// check native method was called
expect(log, <Matcher>[
isMethodCall(
'Auth#setSettings',
arguments: <String, dynamic>{
'appName': defaultFirebaseAppName,
'tenantId': null,
'appVerificationDisabledForTesting': isDisabled,
'userAccessGroup': null,
},
),
]);
if (Platform.isIOS || Platform.isMacOS) {
expect(log, <Matcher>[
isMethodCall(
'Auth#setSettings',
arguments: <String, dynamic>{
'appName': defaultFirebaseAppName,
'tenantId': null,
'appVerificationDisabledForTesting': isDisabled,
'userAccessGroup': groupId,
},
),
]);
}
if (Platform.isAndroid) {
expect(log, <Matcher>[
isMethodCall(
'Auth#setSettings',
arguments: <String, dynamic>{
'appName': defaultFirebaseAppName,
'tenantId': null,
'appVerificationDisabledForTesting': isDisabled,
'phoneNumber': phoneNumber,
'smsCode': smsCode,
'forceRecaptchaFlow': forceRecaptchaFlow,
},
),
]);
}
});

test(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,13 @@ class FirebaseAuthWeb extends FirebaseAuthPlatform {
}

@override
Future<void> setSettings(
{bool? appVerificationDisabledForTesting,
String? userAccessGroup}) async {
Future<void> setSettings({
bool? appVerificationDisabledForTesting,
String? userAccessGroup,
String? phoneNumber,
String? smsCode,
bool? forceRecaptchaFlow,
}) async {
_webAuth!.settings.appVerificationDisabledForTesting =
appVerificationDisabledForTesting;
}
Expand Down