FIRPhoneAuthProviderTests.m 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <GoogleToolboxForMac/GTMNSDictionary+URLArguments.h>
  17. #import <OCMock/OCMock.h>
  18. #import <XCTest/XCTest.h>
  19. #import "FirebaseCommunity/FIRAuth.h"
  20. #import "FIRPhoneAuthProvider.h"
  21. #import "FIRApp.h"
  22. #import "FIRAuth_Internal.h"
  23. #import "FIRAuthAPNSToken.h"
  24. #import "FIRAuthAPNSTokenManager.h"
  25. #import "FIRAuthAppCredential.h"
  26. #import "FIRAuthAppCredentialManager.h"
  27. #import "FIRAuthBackend.h"
  28. #import "FIRAuthCredential_Internal.h"
  29. #import "FIRAuthErrorUtils.h"
  30. #import "FIRAuthGlobalWorkQueue.h"
  31. #import "FIRAuthNotificationManager.h"
  32. #import "FIRAuthRequestConfiguration.h"
  33. #import "FIRAuthUIDelegate.h"
  34. #import "FIRAuthURLPresenter.h"
  35. #import "FIRGetProjectConfigRequest.h"
  36. #import "FIRGetProjectConfigResponse.h"
  37. #import "FIRSendVerificationCodeRequest.h"
  38. #import "FIRSendVerificationCodeResponse.h"
  39. #import "FIROptions.h"
  40. #import "FIRVerifyClientRequest.h"
  41. #import "FIRVerifyClientResponse.h"
  42. #import "OCMStubRecorder+FIRAuthUnitTests.h"
  43. #import "Phone/FIRPhoneAuthCredential_Internal.h"
  44. #import "Phone/NSString+FIRAuth.h"
  45. @import SafariServices;
  46. NS_ASSUME_NONNULL_BEGIN
  47. /** @var kTestPhoneNumber
  48. @brief A testing phone number.
  49. */
  50. static NSString *const kTestPhoneNumber = @"55555555";
  51. /** @var kTestInvalidPhoneNumber
  52. @brief An invalid testing phone number.
  53. */
  54. static NSString *const kTestInvalidPhoneNumber = @"555+!*55555";
  55. /** @var kTestVerificationID
  56. @brief A testing verfication ID.
  57. */
  58. static NSString *const kTestVerificationID = @"verificationID";
  59. /** @var kTestReceipt
  60. @brief A fake receipt for testing.
  61. */
  62. static NSString *const kTestReceipt = @"receipt";
  63. /** @var kTestSecret
  64. @brief A fake secret for testing.
  65. */
  66. static NSString *const kTestSecret = @"secret";
  67. /** @var kTestOldReceipt
  68. @brief A fake old receipt for testing.
  69. */
  70. static NSString *const kTestOldReceipt = @"old_receipt";
  71. /** @var kTestOldSecret
  72. @brief A fake old secret for testing.
  73. */
  74. static NSString *const kTestOldSecret = @"old_secret";
  75. /** @var kTestVerificationCode
  76. @brief A fake verfication code.
  77. */
  78. static NSString *const kTestVerificationCode = @"verificationCode";
  79. /** @var kFakeClientID
  80. @brief A fake client ID.
  81. */
  82. static NSString *const kFakeClientID = @"123456.apps.googleusercontent.com";
  83. /** @var kFakeReverseClientID
  84. @brief The dot-reversed version of the fake client ID.
  85. */
  86. static NSString *const kFakeReverseClientID = @"com.googleusercontent.apps.123456";
  87. /** @var kFakeBundleID
  88. @brief A fake bundle ID.
  89. */
  90. static NSString *const kFakeBundleID = @"com.firebaseapp.example";
  91. /** @var kFakeAPIKey
  92. @brief A fake API key.
  93. */
  94. static NSString *const kFakeAPIKey = @"asdfghjkl";
  95. /** @var kFakeAuthorizedDomain
  96. @brief A fake authorized domain for the app.
  97. */
  98. static NSString *const kFakeAuthorizedDomain = @"test.firebaseapp.com";
  99. /** @var kFakeReCAPTCHAToken
  100. @brief A fake reCAPTCHA token.
  101. */
  102. static NSString *const kFakeReCAPTCHAToken = @"fakeReCAPTCHAToken";
  103. /** @var kFakeRedirectURLStringWithReCAPTCHAToken
  104. @brief The format for a fake redirect URL string that contains the fake reCAPTCHA token above.
  105. */
  106. static NSString *const kFakeRedirectURLStringWithReCAPTCHAToken = @"com.googleusercontent.apps.1"
  107. "23456://firebaseauth/link?deep_link_id=https%3A%2F%2Fexample.firebaseapp.com%2F__%2Fauth%2Fcal"
  108. "lback%3FauthType%3DverifyApp%26recaptchaToken%3DfakeReCAPTCHAToken";
  109. /** @var kFakeRedirectURLStringInvalidClientID
  110. @brief The format for a fake redirect URL string with invalid client error.
  111. */
  112. static NSString *const kFakeRedirectURLStringInvalidClientID = @"com.googleusercontent.apps.1"
  113. "23456://firebaseauth/link?deep_link_id=https%3A%2F%2Fexample.firebaseapp.com%2F__%2Fauth%2Fcal"
  114. "lback%3FfirebaseError%3D%257B%2522code%2522%253A%2522auth%252Finvalid-oauth-client-id%2522%252"
  115. "C%2522message%2522%253A%2522The%2520OAuth%2520client%2520ID%2520provided%2520is%2520either%252"
  116. "0invalid%2520or%2520does%2520not%2520match%2520the%2520specified%2520API%2520key.%2522%257D%26"
  117. "authType%3DverifyApp";
  118. /** @var kFakeRedirectURLStringUnknownError
  119. @brief The format for a fake redirect URL string with unknown error response.
  120. */
  121. static NSString *const kFakeRedirectURLStringUnknownError = @"com.googleusercontent.apps.1"
  122. "23456://firebaseauth/link?deep_link_id=https%3A%2F%2Fexample.firebaseapp.com%2F__%2Fauth%2Fcal"
  123. "lback%3FfirebaseError%3D%257B%2522code%2522%253A%2522auth%252Funknown-error-id%2522%252"
  124. "C%2522message%2522%253A%2522The%2520OAuth%2520client%2520ID%2520provided%2520is%2520either%252"
  125. "0invalid%2520or%2520does%2520not%2520match%2520the%2520specified%2520API%2520key.%2522%257D%26"
  126. "authType%3DverifyApp";
  127. /** @var kTestTimeout
  128. @brief A fake timeout value for waiting for push notification.
  129. */
  130. static const NSTimeInterval kTestTimeout = 5;
  131. /** @var kExpectationTimeout
  132. @brief The maximum time waiting for expectations to fulfill.
  133. */
  134. static const NSTimeInterval kExpectationTimeout = 1;
  135. /** @class FIRPhoneAuthProviderTests
  136. @brief Tests for @c FIRPhoneAuthProvider
  137. */
  138. @interface FIRPhoneAuthProviderTests : XCTestCase
  139. @end
  140. @implementation FIRPhoneAuthProviderTests {
  141. /** @var _mockBackend
  142. @brief The mock @c FIRAuthBackendImplementation .
  143. */
  144. id _mockBackend;
  145. /** @var _provider
  146. @brief The @c FIRPhoneAuthProvider instance under test.
  147. */
  148. FIRPhoneAuthProvider *_provider;
  149. /** @var _mockAuth
  150. @brief The mock @c FIRAuth instance associated with @c _provider .
  151. */
  152. id _mockAuth;
  153. /** @var _mockApp
  154. @brief The mock @c FIRApp instance associated with @c _mockAuth .
  155. */
  156. id _mockApp;
  157. /** @var _mockAPNSTokenManager
  158. @brief The mock @c FIRAuthAPNSTokenManager instance associated with @c _mockAuth .
  159. */
  160. id _mockAPNSTokenManager;
  161. /** @var _mockAppCredentialManager
  162. @brief The mock @c FIRAuthAppCredentialManager instance associated with @c _mockAuth .
  163. */
  164. id _mockAppCredentialManager;
  165. /** @var _mockNotificationManager
  166. @brief The mock @c FIRAuthNotificationManager instance associated with @c _mockAuth .
  167. */
  168. id _mockNotificationManager;
  169. /** @var _mockURLPresenter
  170. @brief The mock @c FIRAuthURLPresenter instance associated with @c _mockAuth .
  171. */
  172. id _mockURLPresenter;
  173. }
  174. - (void)setUp {
  175. [super setUp];
  176. _mockBackend = OCMProtocolMock(@protocol(FIRAuthBackendImplementation));
  177. [FIRAuthBackend setBackendImplementation:_mockBackend];
  178. _mockAuth = OCMClassMock([FIRAuth class]);
  179. _mockApp = OCMClassMock([FIRApp class]);
  180. OCMStub([_mockAuth app]).andReturn(_mockApp);
  181. id mockOptions = OCMClassMock([FIROptions class]);
  182. OCMStub([(FIRApp *)_mockApp options]).andReturn(mockOptions);
  183. OCMStub([mockOptions clientID]).andReturn(kFakeClientID);
  184. _mockAPNSTokenManager = OCMClassMock([FIRAuthAPNSTokenManager class]);
  185. OCMStub([_mockAuth tokenManager]).andReturn(_mockAPNSTokenManager);
  186. _mockAppCredentialManager = OCMClassMock([FIRAuthAppCredentialManager class]);
  187. OCMStub([_mockAuth appCredentialManager]).andReturn(_mockAppCredentialManager);
  188. _mockNotificationManager = OCMClassMock([FIRAuthNotificationManager class]);
  189. OCMStub([_mockAuth notificationManager]).andReturn(_mockNotificationManager);
  190. _mockURLPresenter = OCMClassMock([FIRAuthURLPresenter class]);
  191. OCMStub([_mockAuth authURLPresenter]).andReturn(_mockURLPresenter);
  192. id mockRequestConfiguration = OCMClassMock([FIRAuthRequestConfiguration class]);
  193. OCMStub([_mockAuth requestConfiguration]).andReturn(mockRequestConfiguration);
  194. OCMStub([mockRequestConfiguration APIKey]).andReturn(kFakeAPIKey);
  195. _provider = [FIRPhoneAuthProvider providerWithAuth:_mockAuth];
  196. }
  197. - (void)tearDown {
  198. [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:nil];
  199. [super tearDown];
  200. }
  201. // We're still testing deprecated `verifyPhoneNumber:completion:` extensively.
  202. #pragma clang diagnostic push
  203. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  204. /** @fn testCredentialWithVerificationID
  205. @brief Tests the @c credentialWithToken method to make sure that it returns a valid
  206. FIRAuthCredential instance.
  207. */
  208. - (void)testCredentialWithVerificationID {
  209. FIRPhoneAuthCredential *credential =
  210. [_provider credentialWithVerificationID:kTestVerificationID
  211. verificationCode:kTestVerificationCode];
  212. XCTAssertEqualObjects(credential.verificationID, kTestVerificationID);
  213. XCTAssertEqualObjects(credential.verificationCode, kTestVerificationCode);
  214. XCTAssertNil(credential.temporaryProof);
  215. XCTAssertNil(credential.phoneNumber);
  216. }
  217. /** @fn testVerifyEmptyPhoneNumber
  218. @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an empty phone
  219. number was provided.
  220. */
  221. - (void)testVerifyEmptyPhoneNumber {
  222. // Empty phone number is checked on the client side so no backend RPC is mocked.
  223. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  224. [_provider verifyPhoneNumber:@""
  225. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  226. XCTAssertNotNil(error);
  227. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingPhoneNumber);
  228. [expectation fulfill];
  229. }];
  230. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  231. }
  232. /** @fn testVerifyInvalidPhoneNumber
  233. @brief Tests a failed invocation @c verifyPhoneNumber:completion: because an invalid phone
  234. number was provided.
  235. */
  236. - (void)testVerifyInvalidPhoneNumber {
  237. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  238. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  239. OCMStub([_mockAppCredentialManager credential])
  240. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  241. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  242. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  243. FIRSendVerificationCodeResponseCallback callback) {
  244. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  245. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  246. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  247. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  248. callback(nil, [FIRAuthErrorUtils invalidPhoneNumberErrorWithMessage:nil]);
  249. });
  250. });
  251. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  252. [_provider verifyPhoneNumber:kTestPhoneNumber
  253. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  254. XCTAssertTrue([NSThread isMainThread]);
  255. XCTAssertNil(verificationID);
  256. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidPhoneNumber);
  257. [expectation fulfill];
  258. }];
  259. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  260. OCMVerifyAll(_mockBackend);
  261. OCMVerifyAll(_mockNotificationManager);
  262. OCMVerifyAll(_mockAppCredentialManager);
  263. }
  264. /** @fn testVerifyPhoneNumber
  265. @brief Tests a successful invocation of @c verifyPhoneNumber:completion:.
  266. */
  267. - (void)testVerifyPhoneNumber {
  268. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  269. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  270. OCMStub([_mockAppCredentialManager credential])
  271. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  272. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  273. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  274. FIRSendVerificationCodeResponseCallback callback) {
  275. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  276. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  277. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  278. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  279. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  280. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  281. callback(mockSendVerificationCodeResponse, nil);
  282. });
  283. });
  284. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  285. [_provider verifyPhoneNumber:kTestPhoneNumber
  286. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  287. XCTAssertTrue([NSThread isMainThread]);
  288. XCTAssertNil(error);
  289. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  290. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  291. [expectation fulfill];
  292. }];
  293. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  294. OCMVerifyAll(_mockBackend);
  295. OCMVerifyAll(_mockNotificationManager);
  296. OCMVerifyAll(_mockAppCredentialManager);
  297. }
  298. /** @fn testVerifyPhoneNumberUIDelegate
  299. @brief Tests a successful invocation of @c verifyPhoneNumber:UIDelegate:completion:.
  300. */
  301. - (void)testVerifyPhoneNumberUIDelegate {
  302. id mockBundle = OCMClassMock([NSBundle class]);
  303. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  304. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  305. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ kFakeReverseClientID ] } ]);
  306. OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
  307. // Simulate missing app token error.
  308. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  309. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  310. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  311. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  312. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) {
  313. NSError *error = [NSError errorWithDomain:FIRAuthErrorDomain
  314. code:FIRAuthErrorCodeMissingAppToken
  315. userInfo:nil];
  316. callback(nil, error);
  317. });
  318. OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
  319. .andCallBlock2(^(FIRGetProjectConfigRequest *request,
  320. FIRGetProjectConfigResponseCallback callback) {
  321. XCTAssertNotNil(request);
  322. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  323. id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
  324. OCMStub([mockGetProjectConfigResponse authorizedDomains]).
  325. andReturn(@[ kFakeAuthorizedDomain]);
  326. callback(mockGetProjectConfigResponse, nil);
  327. });
  328. });
  329. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  330. // Expect view controller presentation by UIDelegate.
  331. OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
  332. UIDelegate:mockUIDelegate
  333. callbackMatcher:OCMOCK_ANY
  334. completion:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
  335. __unsafe_unretained id unretainedArgument;
  336. // Indices 0 and 1 indicate the hidden arguments self and _cmd.
  337. // `presentURL` is at index 2.
  338. [invocation getArgument:&unretainedArgument atIndex:2];
  339. NSURL *presentURL = unretainedArgument;
  340. XCTAssertEqualObjects(presentURL.scheme, @"https");
  341. XCTAssertEqualObjects(presentURL.host, kFakeAuthorizedDomain);
  342. XCTAssertEqualObjects(presentURL.path, @"/__/auth/handler");
  343. NSDictionary *params = [NSDictionary gtm_dictionaryWithHttpArgumentsString:presentURL.query];
  344. XCTAssertEqualObjects(params[@"ibi"], kFakeBundleID);
  345. XCTAssertEqualObjects(params[@"clientId"], kFakeClientID);
  346. XCTAssertEqualObjects(params[@"apiKey"], kFakeAPIKey);
  347. XCTAssertEqualObjects(params[@"authType"], @"verifyApp");
  348. XCTAssertNotNil(params[@"v"]);
  349. // `callbackMatcher` is at index 4
  350. [invocation getArgument:&unretainedArgument atIndex:4];
  351. FIRAuthURLCallbackMatcher callbackMatcher = unretainedArgument;
  352. NSURLComponents *originalComponents =
  353. [[NSURLComponents alloc] initWithString:kFakeRedirectURLStringWithReCAPTCHAToken];
  354. XCTAssertTrue(callbackMatcher([originalComponents URL]));
  355. NSURLComponents *components = [originalComponents copy];
  356. components.query = @"https";
  357. XCTAssertFalse(callbackMatcher([components URL]));
  358. components = [originalComponents copy];
  359. components.host = @"badhost";
  360. XCTAssertFalse(callbackMatcher([components URL]));
  361. components = [originalComponents copy];
  362. components.path = @"badpath";
  363. XCTAssertFalse(callbackMatcher([components URL]));
  364. components = [originalComponents copy];
  365. components.query = @"badquery";
  366. XCTAssertFalse(callbackMatcher([components URL]));
  367. // `completion` is at index 5
  368. [invocation getArgument:&unretainedArgument atIndex:5];
  369. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  370. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  371. completion([NSURL URLWithString:kFakeRedirectURLStringWithReCAPTCHAToken], nil);
  372. });
  373. });
  374. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  375. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  376. FIRSendVerificationCodeResponseCallback callback) {
  377. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  378. XCTAssertNil(request.appCredential);
  379. XCTAssertEqualObjects(request.reCAPTCHAToken, kFakeReCAPTCHAToken);
  380. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  381. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  382. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  383. callback(mockSendVerificationCodeResponse, nil);
  384. });
  385. });
  386. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  387. [_provider verifyPhoneNumber:kTestPhoneNumber
  388. UIDelegate:mockUIDelegate
  389. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  390. XCTAssertTrue([NSThread isMainThread]);
  391. XCTAssertNil(error);
  392. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  393. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  394. [expectation fulfill];
  395. }];
  396. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  397. OCMVerifyAll(_mockBackend);
  398. OCMVerifyAll(_mockNotificationManager);
  399. }
  400. /** @fn testVerifyPhoneNumberUIDelegateInvalidClientID
  401. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  402. invalid client ID.
  403. */
  404. - (void)testVerifyPhoneNumberUIDelegateInvalidClientID {
  405. id mockBundle = OCMClassMock([NSBundle class]);
  406. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  407. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  408. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ kFakeReverseClientID ] } ]);
  409. OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
  410. // Simulate missing app token error.
  411. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  412. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  413. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  414. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  415. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) {
  416. NSError *error = [NSError errorWithDomain:FIRAuthErrorDomain
  417. code:FIRAuthErrorCodeMissingAppToken
  418. userInfo:nil];
  419. callback(nil, error);
  420. });
  421. OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
  422. .andCallBlock2(^(FIRGetProjectConfigRequest *request,
  423. FIRGetProjectConfigResponseCallback callback) {
  424. XCTAssertNotNil(request);
  425. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  426. id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
  427. OCMStub([mockGetProjectConfigResponse authorizedDomains]).
  428. andReturn(@[ kFakeAuthorizedDomain]);
  429. callback(mockGetProjectConfigResponse, nil);
  430. });
  431. });
  432. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  433. // Expect view controller presentation by UIDelegate.
  434. OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
  435. UIDelegate:mockUIDelegate
  436. callbackMatcher:OCMOCK_ANY
  437. completion:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
  438. __unsafe_unretained id unretainedArgument;
  439. // Indices 0 and 1 indicate the hidden arguments self and _cmd.
  440. // `completion` is at index 5
  441. [invocation getArgument:&unretainedArgument atIndex:5];
  442. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  443. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  444. completion([NSURL URLWithString:kFakeRedirectURLStringInvalidClientID], nil);
  445. });
  446. });
  447. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  448. [_provider verifyPhoneNumber:kTestPhoneNumber
  449. UIDelegate:mockUIDelegate
  450. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  451. XCTAssertTrue([NSThread isMainThread]);
  452. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidClientID);
  453. XCTAssertNil(verificationID);
  454. [expectation fulfill];
  455. }];
  456. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  457. OCMVerifyAll(_mockBackend);
  458. OCMVerifyAll(_mockNotificationManager);
  459. }
  460. /** @fn testVerifyPhoneNumberUIDelegateUnexpectedError
  461. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  462. invalid client ID.
  463. */
  464. - (void)testVerifyPhoneNumberUIDelegateUnexpectedError {
  465. id mockBundle = OCMClassMock([NSBundle class]);
  466. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  467. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  468. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ kFakeReverseClientID ] } ]);
  469. OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
  470. // Simulate missing app token error.
  471. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  472. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  473. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  474. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  475. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) {
  476. NSError *error = [NSError errorWithDomain:FIRAuthErrorDomain
  477. code:FIRAuthErrorCodeMissingAppToken
  478. userInfo:nil];
  479. callback(nil, error);
  480. });
  481. OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
  482. .andCallBlock2(^(FIRGetProjectConfigRequest *request,
  483. FIRGetProjectConfigResponseCallback callback) {
  484. XCTAssertNotNil(request);
  485. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  486. id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
  487. OCMStub([mockGetProjectConfigResponse authorizedDomains]).
  488. andReturn(@[ kFakeAuthorizedDomain]);
  489. callback(mockGetProjectConfigResponse, nil);
  490. });
  491. });
  492. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  493. // Expect view controller presentation by UIDelegate.
  494. OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
  495. UIDelegate:mockUIDelegate
  496. callbackMatcher:OCMOCK_ANY
  497. completion:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
  498. __unsafe_unretained id unretainedArgument;
  499. // Indices 0 and 1 indicate the hidden arguments self and _cmd.
  500. // `completion` is at index 5
  501. [invocation getArgument:&unretainedArgument atIndex:5];
  502. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  503. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  504. completion([NSURL URLWithString:kFakeRedirectURLStringUnknownError], nil);
  505. });
  506. });
  507. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  508. [_provider verifyPhoneNumber:kTestPhoneNumber
  509. UIDelegate:mockUIDelegate
  510. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  511. XCTAssertTrue([NSThread isMainThread]);
  512. XCTAssertEqual(error.code, FIRAuthErrorCodeAppVerificationUserInteractionFailure);
  513. XCTAssertNil(verificationID);
  514. [expectation fulfill];
  515. }];
  516. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  517. OCMVerifyAll(_mockBackend);
  518. OCMVerifyAll(_mockNotificationManager);
  519. }
  520. /** @fn testVerifyPhoneNumberUIDelegateRaiseException
  521. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  522. exception.
  523. */
  524. - (void)testVerifyPhoneNumberUIDelegateRaiseException {
  525. id mockBundle = OCMClassMock([NSBundle class]);
  526. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  527. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  528. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ @"badscheme" ] } ]);
  529. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  530. XCTAssertThrows([_provider verifyPhoneNumber:kTestPhoneNumber
  531. UIDelegate:mockUIDelegate
  532. completion:^(NSString *_Nullable verificationID,
  533. NSError *_Nullable error) {
  534. XCTFail(@"Shouldn't call completion here.");
  535. }]);
  536. }
  537. /** @fn testNotForwardingNotification
  538. @brief Tests returning an error for the app failing to forward notification.
  539. */
  540. - (void)testNotForwardingNotification {
  541. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  542. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(NO); });
  543. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  544. [_provider verifyPhoneNumber:kTestPhoneNumber
  545. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  546. XCTAssertTrue([NSThread isMainThread]);
  547. XCTAssertNil(verificationID);
  548. XCTAssertEqual(error.code, FIRAuthErrorCodeNotificationNotForwarded);
  549. [expectation fulfill];
  550. }];
  551. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  552. OCMVerifyAll(_mockNotificationManager);
  553. }
  554. /** @fn testMissingAPNSToken
  555. @brief Tests returning an error for the app failing to provide an APNS device token.
  556. */
  557. - (void)testMissingAPNSToken {
  558. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  559. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  560. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  561. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  562. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(nil, nil); });
  563. // Expect verify client request to the backend wth empty token.
  564. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  565. .andCallBlock2(^(FIRVerifyClientRequest *request,
  566. FIRVerifyClientResponseCallback callback) {
  567. XCTAssertNil(request.appToken);
  568. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  569. // The backend is supposed to return an error.
  570. callback(nil, [NSError errorWithDomain:FIRAuthErrorDomain
  571. code:FIRAuthErrorCodeMissingAppToken
  572. userInfo:nil]);
  573. });
  574. });
  575. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  576. [_provider verifyPhoneNumber:kTestPhoneNumber
  577. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  578. XCTAssertTrue([NSThread isMainThread]);
  579. XCTAssertNil(verificationID);
  580. XCTAssertEqualObjects(error.domain, FIRAuthErrorDomain);
  581. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingAppToken);
  582. [expectation fulfill];
  583. }];
  584. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  585. OCMVerifyAll(_mockNotificationManager);
  586. OCMVerifyAll(_mockAppCredentialManager);
  587. OCMVerifyAll(_mockAPNSTokenManager);
  588. }
  589. /** @fn testVerifyClient
  590. @brief Tests verifying client before sending verification code.
  591. */
  592. - (void)testVerifyClient {
  593. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  594. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  595. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  596. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  597. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  598. type:FIRAuthAPNSTokenTypeProd];
  599. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  600. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  601. // Expect verify client request to the backend.
  602. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  603. .andCallBlock2(^(FIRVerifyClientRequest *request,
  604. FIRVerifyClientResponseCallback callback) {
  605. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  606. XCTAssertFalse(request.isSandbox);
  607. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  608. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  609. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  610. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  611. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  612. callback(mockVerifyClientResponse, nil);
  613. });
  614. });
  615. // Mock receiving of push notification.
  616. OCMExpect([[_mockAppCredentialManager ignoringNonObjectArgs]
  617. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  618. .andCallIdDoubleIdBlock(^(NSString *receipt,
  619. NSTimeInterval timeout,
  620. FIRAuthAppCredentialCallback callback) {
  621. XCTAssertEqualObjects(receipt, kTestReceipt);
  622. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  623. // into the block either, so we can't verify it here.
  624. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  625. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  626. });
  627. });
  628. // Expect send verification code request to the backend.
  629. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  630. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  631. FIRSendVerificationCodeResponseCallback callback) {
  632. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  633. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  634. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  635. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  636. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  637. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  638. callback(mockSendVerificationCodeResponse, nil);
  639. });
  640. });
  641. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  642. [_provider verifyPhoneNumber:kTestPhoneNumber
  643. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  644. XCTAssertTrue([NSThread isMainThread]);
  645. XCTAssertNil(error);
  646. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  647. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  648. [expectation fulfill];
  649. }];
  650. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  651. OCMVerifyAll(_mockBackend);
  652. OCMVerifyAll(_mockNotificationManager);
  653. OCMVerifyAll(_mockAppCredentialManager);
  654. OCMVerifyAll(_mockAPNSTokenManager);
  655. }
  656. /** @fn testSendVerificationCodeFailedRetry
  657. @brief Tests failed retry after failing to send verification code.
  658. */
  659. - (void)testSendVerificationCodeFailedRetry {
  660. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  661. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  662. // Expect twice due to null check consumes one expectation.
  663. OCMExpect([_mockAppCredentialManager credential])
  664. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  665. secret:kTestOldSecret]);
  666. OCMExpect([_mockAppCredentialManager credential])
  667. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  668. secret:kTestOldSecret]);
  669. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  670. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  671. type:FIRAuthAPNSTokenTypeProd];
  672. // Expect first sendVerificationCode request to the backend, with request containing old app
  673. // credential.
  674. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  675. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  676. FIRSendVerificationCodeResponseCallback callback) {
  677. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  678. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  679. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  680. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  681. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  682. });
  683. });
  684. // Expect send verification code request to the backend, with request containing new app
  685. // credential data.
  686. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  687. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  688. FIRSendVerificationCodeResponseCallback callback) {
  689. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  690. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  691. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  692. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  693. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  694. });
  695. });
  696. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  697. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  698. // Expect verify client request to the backend.
  699. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  700. .andCallBlock2(^(FIRVerifyClientRequest *request,
  701. FIRVerifyClientResponseCallback callback) {
  702. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  703. XCTAssertFalse(request.isSandbox);
  704. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  705. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  706. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  707. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  708. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  709. callback(mockVerifyClientResponse, nil);
  710. });
  711. });
  712. // Mock receiving of push notification.
  713. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  714. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  715. .andCallIdDoubleIdBlock(^(NSString *receipt,
  716. NSTimeInterval timeout,
  717. FIRAuthAppCredentialCallback callback) {
  718. XCTAssertEqualObjects(receipt, kTestReceipt);
  719. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  720. // into the block either, so we can't verify it here.
  721. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  722. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  723. });
  724. });
  725. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  726. [_provider verifyPhoneNumber:kTestPhoneNumber
  727. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  728. XCTAssertTrue([NSThread isMainThread]);
  729. XCTAssertNil(verificationID);
  730. XCTAssertEqual(error.code, FIRAuthErrorCodeInternalError);
  731. [expectation fulfill];
  732. }];
  733. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  734. OCMVerifyAll(_mockBackend);
  735. OCMVerifyAll(_mockNotificationManager);
  736. OCMVerifyAll(_mockAppCredentialManager);
  737. OCMVerifyAll(_mockAPNSTokenManager);
  738. }
  739. /** @fn testSendVerificationCodeSuccessFulRetry
  740. @brief Tests successful retry after failing to send verification code.
  741. */
  742. - (void)testSendVerificationCodeSuccessFulRetry {
  743. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  744. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  745. // Expect twice due to null check consumes one expectation.
  746. OCMExpect([_mockAppCredentialManager credential])
  747. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  748. secret:kTestOldSecret]);
  749. OCMExpect([_mockAppCredentialManager credential])
  750. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  751. secret:kTestOldSecret]);
  752. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  753. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  754. type:FIRAuthAPNSTokenTypeProd];
  755. // Expect first sendVerificationCode request to the backend, with request containing old app
  756. // credential.
  757. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  758. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  759. FIRSendVerificationCodeResponseCallback callback) {
  760. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  761. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  762. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  763. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  764. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  765. });
  766. });
  767. // Expect send verification code request to the backend, with request containing new app
  768. // credential data.
  769. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  770. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  771. FIRSendVerificationCodeResponseCallback callback) {
  772. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  773. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  774. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  775. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  776. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  777. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  778. callback(mockSendVerificationCodeResponse, nil);
  779. });
  780. });
  781. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  782. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  783. // Expect verify client request to the backend.
  784. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  785. .andCallBlock2(^(FIRVerifyClientRequest *request,
  786. FIRVerifyClientResponseCallback callback) {
  787. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  788. XCTAssertFalse(request.isSandbox);
  789. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  790. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  791. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  792. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  793. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  794. callback(mockVerifyClientResponse, nil);
  795. });
  796. });
  797. // Mock receiving of push notification.
  798. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  799. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  800. .andCallIdDoubleIdBlock(^(NSString *receipt,
  801. NSTimeInterval timeout,
  802. FIRAuthAppCredentialCallback callback) {
  803. XCTAssertEqualObjects(receipt, kTestReceipt);
  804. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  805. // into the block either, so we can't verify it here.
  806. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  807. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  808. });
  809. });
  810. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  811. [_provider verifyPhoneNumber:kTestPhoneNumber
  812. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  813. XCTAssertNil(error);
  814. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  815. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  816. [expectation fulfill];
  817. }];
  818. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  819. OCMVerifyAll(_mockBackend);
  820. OCMVerifyAll(_mockNotificationManager);
  821. OCMVerifyAll(_mockAppCredentialManager);
  822. OCMVerifyAll(_mockAPNSTokenManager);
  823. }
  824. #pragma clang diagnostic pop // ignored "-Wdeprecated-declarations"
  825. @end
  826. NS_ASSUME_NONNULL_END