diff options
-rw-r--r-- | Example/Auth/Sample/SettingsViewController.m | 17 | ||||
-rw-r--r-- | Example/Auth/Tests/FIRPhoneAuthProviderTests.m | 75 | ||||
-rw-r--r-- | Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m | 15 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuth.m | 2 | ||||
-rw-r--r-- | Firebase/Auth/Source/FIRAuthSettings.m | 29 | ||||
-rw-r--r-- | Firebase/Auth/Source/Public/FIRAuth.h | 6 | ||||
-rw-r--r-- | Firebase/Auth/Source/Public/FIRAuthSettings.h | 30 | ||||
-rw-r--r-- | Firebase/Auth/Source/Public/FirebaseAuth.h | 1 | ||||
-rw-r--r-- | Firebase/Auth/Source/RPCs/FIRAuthBackend.m | 14 |
9 files changed, 189 insertions, 0 deletions
diff --git a/Example/Auth/Sample/SettingsViewController.m b/Example/Auth/Sample/SettingsViewController.m index 24a6513..4815f32 100644 --- a/Example/Auth/Sample/SettingsViewController.m +++ b/Example/Auth/Sample/SettingsViewController.m @@ -214,9 +214,26 @@ static NSString *truncatedString(NSString *string, NSUInteger length) { [weakSelf loadTableView]; }], ]], + [StaticContentTableViewSection sectionWithTitle:@"Auth Settings" cells:@[ + [StaticContentTableViewCell cellWithTitle:@"Disable App Verification (Phone)" + value:[AppManager auth].settings. + appVerificationDisabledForTesting ? @"Yes" : @"No" + action:^{ + [weakSelf toggleDisableAppVerification]; + [weakSelf loadTableView]; + }], + ]], ]]; } +/** @fn toggleDisableAppVerification + @brief Toggles the appVerificationDisabledForTesting flag on the current Auth instance. + */ +- (void)toggleDisableAppVerification { + [AppManager auth].settings.appVerificationDisabledForTesting = + ![AppManager auth].settings.appVerificationDisabledForTesting; +} + /** @fn toggleAPIHostWithRequestClassName: @brief Toggles the host name of the server that handles RPCs. @param requestClassName The name of the RPC request class. diff --git a/Example/Auth/Tests/FIRPhoneAuthProviderTests.m b/Example/Auth/Tests/FIRPhoneAuthProviderTests.m index e00fa1f..96432c7 100644 --- a/Example/Auth/Tests/FIRPhoneAuthProviderTests.m +++ b/Example/Auth/Tests/FIRPhoneAuthProviderTests.m @@ -31,6 +31,7 @@ #import "FIRAuthGlobalWorkQueue.h" #import "FIRAuthNotificationManager.h" #import "FIRAuthRequestConfiguration.h" +#import "FIRAuthSettings.h" #import "FIRAuthUIDelegate.h" #import "FIRAuthURLPresenter.h" #import "FIRAuthWebUtils.h" @@ -362,6 +363,80 @@ static const NSTimeInterval kExpectationTimeout = 2; OCMVerifyAll(_mockAppCredentialManager); } +/** @fn testVerifyPhoneNumberInTestMode + @brief Tests a successful invocation of @c verifyPhoneNumber:completion: when app verification + is disabled. + */ +- (void)testVerifyPhoneNumberInTestMode { + // Disable app verification. + FIRAuthSettings *settings = [[FIRAuthSettings alloc] init]; + settings.appVerificationDisabledForTesting = YES; + OCMStub([_mockAuth settings]).andReturn(settings); + OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY]) + .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); }); + OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRSendVerificationCodeRequest *request, + FIRSendVerificationCodeResponseCallback callback) { + XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber); + // Assert that the app credential is nil when in test mode. + XCTAssertNil(request.appCredential); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]); + OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID); + callback(mockSendVerificationCodeResponse, nil); + }); + }); + + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [_provider verifyPhoneNumber:kTestPhoneNumber + completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(error); + XCTAssertEqualObjects(verificationID, kTestVerificationID); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); + OCMVerifyAll(_mockNotificationManager); + OCMVerifyAll(_mockAppCredentialManager); +} + +/** @fn testVerifyPhoneNumberInTestModeFailure + @brief Tests a failed invocation of @c verifyPhoneNumber:completion: when app verification + is disabled. + */ +- (void)testVerifyPhoneNumberInTestModeFailure { + // Disable app verification. + FIRAuthSettings *settings = [[FIRAuthSettings alloc] init]; + settings.appVerificationDisabledForTesting = YES; + OCMStub([_mockAuth settings]).andReturn(settings); + OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY]) + .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); }); + OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]]) + .andCallBlock2(^(FIRSendVerificationCodeRequest *request, + FIRSendVerificationCodeResponseCallback callback) { + XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber); + // Assert that the app credential is nil when in test mode. + XCTAssertNil(request.appCredential); + dispatch_async(FIRAuthGlobalWorkQueue(), ^() { + callback(nil, [FIRAuthErrorUtils networkErrorWithUnderlyingError:[NSError new]]); + }); + }); + + XCTestExpectation *expectation = [self expectationWithDescription:@"callback"]; + [_provider verifyPhoneNumber:kTestPhoneNumber + completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) { + XCTAssertTrue([NSThread isMainThread]); + XCTAssertNil(verificationID); + XCTAssertEqual(error.code, FIRAuthErrorCodeNetworkError); + [expectation fulfill]; + }]; + [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil]; + OCMVerifyAll(_mockBackend); + OCMVerifyAll(_mockNotificationManager); + OCMVerifyAll(_mockAppCredentialManager); +} + /** @fn testVerifyPhoneNumberUIDelegate @brief Tests a successful invocation of @c verifyPhoneNumber:UIDelegate:completion:. */ diff --git a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m index 948a515..2d06a15 100644 --- a/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m +++ b/Firebase/Auth/Source/AuthProviders/Phone/FIRPhoneAuthProvider.m @@ -29,6 +29,7 @@ #import "FIRAuthNotificationManager.h" #import "FIRAuthErrorUtils.h" #import "FIRAuthBackend.h" +#import "FIRAuthSettings.h" #import "FIRAuthWebUtils.h" #import "FirebaseAuthVersion.h" #import <FirebaseCore/FIROptions.h> @@ -364,6 +365,20 @@ NSString *const kReCAPTCHAURLStringFormat = @"https://%@/__/auth/handler?"; - (void)verifyClientAndSendVerificationCodeToPhoneNumber:(NSString *)phoneNumber retryOnInvalidAppCredential:(BOOL)retryOnInvalidAppCredential callback:(FIRVerificationResultCallback)callback { + if (_auth.settings.isAppVerificationDisabledForTesting) { + FIRSendVerificationCodeRequest *request = + [[FIRSendVerificationCodeRequest alloc] initWithPhoneNumber:phoneNumber + appCredential:nil + reCAPTCHAToken:nil + requestConfiguration: + _auth.requestConfiguration]; + [FIRAuthBackend sendVerificationCode:request + callback:^(FIRSendVerificationCodeResponse *_Nullable response, + NSError *_Nullable error) { + callback(response.verificationID, error); + }]; + return; + } [self verifyClientWithCompletion:^(FIRAuthAppCredential *_Nullable appCredential, NSError *_Nullable error) { if (error) { diff --git a/Firebase/Auth/Source/FIRAuth.m b/Firebase/Auth/Source/FIRAuth.m index 82a7c02..c83a19a 100644 --- a/Firebase/Auth/Source/FIRAuth.m +++ b/Firebase/Auth/Source/FIRAuth.m @@ -33,6 +33,7 @@ #import "FIRAuthGlobalWorkQueue.h" #import "FIRAuthKeychain.h" #import "FIRAuthOperationType.h" +#import "FIRAuthSettings.h" #import "FIRUser_Internal.h" #import "FirebaseAuth.h" #import "FIRAuthBackend.h" @@ -434,6 +435,7 @@ static NSMutableDictionary *gKeychainServiceNameForAppName; if (self) { _listenerHandles = [NSMutableArray array]; _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:APIKey]; + _settings = [[FIRAuthSettings alloc] init]; _firebaseAppName = [appName copy]; #if TARGET_OS_IOS UIApplication *application = [UIApplication sharedApplication]; diff --git a/Firebase/Auth/Source/FIRAuthSettings.m b/Firebase/Auth/Source/FIRAuthSettings.m new file mode 100644 index 0000000..575bb3c --- /dev/null +++ b/Firebase/Auth/Source/FIRAuthSettings.m @@ -0,0 +1,29 @@ +/* + * Copyright 2018 Google + * + * 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/LICENSE2.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. + */ + +#import "FIRAuthSettings.h" + +@implementation FIRAuthSettings + +- (instancetype)init { + self = [super init]; + if (self) { + _appVerificationDisabledForTesting = NO; + } + return self; +} + +@end diff --git a/Firebase/Auth/Source/Public/FIRAuth.h b/Firebase/Auth/Source/Public/FIRAuth.h index c262c49..2901fca 100644 --- a/Firebase/Auth/Source/Public/FIRAuth.h +++ b/Firebase/Auth/Source/Public/FIRAuth.h @@ -28,6 +28,7 @@ @class FIRAuth; @class FIRAuthCredential; @class FIRAuthDataResult; +@class FIRAuthSettings; @class FIRUser; @protocol FIRAuthStateListener; @@ -288,6 +289,11 @@ NS_SWIFT_NAME(Auth) */ @property (nonatomic, copy, nullable) NSString *languageCode; +/** @property settings + @brief Contains settings related to the auth object. + */ +@property (nonatomic, copy, nullable) FIRAuthSettings *settings; + #if TARGET_OS_IOS /** @property APNSToken @brief The APNs token used for phone number authentication. The type of the token (production diff --git a/Firebase/Auth/Source/Public/FIRAuthSettings.h b/Firebase/Auth/Source/Public/FIRAuthSettings.h new file mode 100644 index 0000000..d3fee3e --- /dev/null +++ b/Firebase/Auth/Source/Public/FIRAuthSettings.h @@ -0,0 +1,30 @@ +/* + * Copyright 2018 Google + * + * 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. + */ + +#import <Foundation/Foundation.h> + +/** @class FIRAuthSettings + @brief Determines settings related to an auth object. + */ +@interface FIRAuthSettings : NSObject + +/** @property appVerificationDisabledForTesting + @brief Flag to determine whether app verification should be disabled for testing or not. + */ +@property (nonatomic, assign, getter=isAppVerificationDisabledForTesting) BOOL + appVerificationDisabledForTesting; + +@end diff --git a/Firebase/Auth/Source/Public/FirebaseAuth.h b/Firebase/Auth/Source/Public/FirebaseAuth.h index cd7e4a4..c8837f8 100644 --- a/Firebase/Auth/Source/Public/FirebaseAuth.h +++ b/Firebase/Auth/Source/Public/FirebaseAuth.h @@ -39,4 +39,5 @@ #import "FIRPhoneAuthCredential.h" #import "FIRPhoneAuthProvider.h" #import "FIRAuthAPNSTokenType.h" +#import "FIRAuthSettings.h" #endif diff --git a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m index e380e34..ff8f2f0 100644 --- a/Firebase/Auth/Source/RPCs/FIRAuthBackend.m +++ b/Firebase/Auth/Source/RPCs/FIRAuthBackend.m @@ -350,6 +350,13 @@ static NSString *const kQuoutaExceededErrorMessage = @"QUOTA_EXCEEDED"; */ static NSString *const kAppNotVerifiedErrorMessage = @"APP_NOT_VERIFIED"; +/** @var kMissingClientIdentifier + @brief This is the error message the server will respond with if Firebase could not verify the + app during a phone authentication flow when a real phone number is used and app verification + is disabled for testing. + */ +static NSString *const kMissingClientIdentifier = @"MISSING_CLIENT_IDENTIFIER"; + /** @var kCaptchaCheckFailedErrorMessage @brief This is the error message the server will respond with if the reCAPTCHA token provided is invalid. @@ -1073,6 +1080,13 @@ static id<FIRAuthBackendImplementation> gBackendImplementation; return [FIRAuthErrorUtils appNotVerifiedErrorWithMessage:serverErrorMessage]; } + if ([shortErrorMessage isEqualToString:kMissingClientIdentifier]) { + return [FIRAuthErrorUtils appNotVerifiedErrorWithMessage:@"Missing app verification via" + " reCAPTCHA or APNS token. Please verify that appVerificationDisabledForTesting is not" + " enabled when testing with a phone number that is not marked as a test Phone number in the" + " app console."]; + } + if ([shortErrorMessage isEqualToString:kCaptchaCheckFailedErrorMessage]) { return [FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:serverErrorMessage]; } |