FIRPhoneAuthProviderTests.m 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  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. NSMutableString *redirectURL =
  353. [NSMutableString stringWithString:kFakeRedirectURLStringWithReCAPTCHAToken];
  354. // Verify that the URL is rejected by the callback matcher without the event ID.
  355. XCTAssertFalse(callbackMatcher([NSURL URLWithString:redirectURL]));
  356. [redirectURL appendString:@"%26eventId%3D"];
  357. [redirectURL appendString:params[@"eventId"]];
  358. NSURLComponents *originalComponents = [[NSURLComponents alloc] initWithString:redirectURL];
  359. // Verify that the URL is accepted by the callback matcher with the matching event ID.
  360. XCTAssertTrue(callbackMatcher([originalComponents URL]));
  361. NSURLComponents *components = [originalComponents copy];
  362. components.query = @"https";
  363. XCTAssertFalse(callbackMatcher([components URL]));
  364. components = [originalComponents copy];
  365. components.host = @"badhost";
  366. XCTAssertFalse(callbackMatcher([components URL]));
  367. components = [originalComponents copy];
  368. components.path = @"badpath";
  369. XCTAssertFalse(callbackMatcher([components URL]));
  370. components = [originalComponents copy];
  371. components.query = @"badquery";
  372. XCTAssertFalse(callbackMatcher([components URL]));
  373. // `completion` is at index 5
  374. [invocation getArgument:&unretainedArgument atIndex:5];
  375. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  376. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  377. completion([NSURL URLWithString:kFakeRedirectURLStringWithReCAPTCHAToken], nil);
  378. });
  379. });
  380. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  381. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  382. FIRSendVerificationCodeResponseCallback callback) {
  383. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  384. XCTAssertNil(request.appCredential);
  385. XCTAssertEqualObjects(request.reCAPTCHAToken, kFakeReCAPTCHAToken);
  386. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  387. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  388. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  389. callback(mockSendVerificationCodeResponse, nil);
  390. });
  391. });
  392. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  393. [_provider verifyPhoneNumber:kTestPhoneNumber
  394. UIDelegate:mockUIDelegate
  395. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  396. XCTAssertTrue([NSThread isMainThread]);
  397. XCTAssertNil(error);
  398. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  399. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  400. [expectation fulfill];
  401. }];
  402. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  403. OCMVerifyAll(_mockBackend);
  404. OCMVerifyAll(_mockNotificationManager);
  405. }
  406. /** @fn testVerifyPhoneNumberUIDelegateInvalidClientID
  407. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  408. invalid client ID.
  409. */
  410. - (void)testVerifyPhoneNumberUIDelegateInvalidClientID {
  411. id mockBundle = OCMClassMock([NSBundle class]);
  412. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  413. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  414. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ kFakeReverseClientID ] } ]);
  415. OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
  416. // Simulate missing app token error.
  417. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  418. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  419. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  420. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  421. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) {
  422. NSError *error = [NSError errorWithDomain:FIRAuthErrorDomain
  423. code:FIRAuthErrorCodeMissingAppToken
  424. userInfo:nil];
  425. callback(nil, error);
  426. });
  427. OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
  428. .andCallBlock2(^(FIRGetProjectConfigRequest *request,
  429. FIRGetProjectConfigResponseCallback callback) {
  430. XCTAssertNotNil(request);
  431. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  432. id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
  433. OCMStub([mockGetProjectConfigResponse authorizedDomains]).
  434. andReturn(@[ kFakeAuthorizedDomain]);
  435. callback(mockGetProjectConfigResponse, nil);
  436. });
  437. });
  438. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  439. // Expect view controller presentation by UIDelegate.
  440. OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
  441. UIDelegate:mockUIDelegate
  442. callbackMatcher:OCMOCK_ANY
  443. completion:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
  444. __unsafe_unretained id unretainedArgument;
  445. // Indices 0 and 1 indicate the hidden arguments self and _cmd.
  446. // `completion` is at index 5
  447. [invocation getArgument:&unretainedArgument atIndex:5];
  448. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  449. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  450. completion([NSURL URLWithString:kFakeRedirectURLStringInvalidClientID], nil);
  451. });
  452. });
  453. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  454. [_provider verifyPhoneNumber:kTestPhoneNumber
  455. UIDelegate:mockUIDelegate
  456. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  457. XCTAssertTrue([NSThread isMainThread]);
  458. XCTAssertEqual(error.code, FIRAuthErrorCodeInvalidClientID);
  459. XCTAssertNil(verificationID);
  460. [expectation fulfill];
  461. }];
  462. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  463. OCMVerifyAll(_mockBackend);
  464. OCMVerifyAll(_mockNotificationManager);
  465. }
  466. /** @fn testVerifyPhoneNumberUIDelegateUnexpectedError
  467. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  468. invalid client ID.
  469. */
  470. - (void)testVerifyPhoneNumberUIDelegateUnexpectedError {
  471. id mockBundle = OCMClassMock([NSBundle class]);
  472. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  473. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  474. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ kFakeReverseClientID ] } ]);
  475. OCMStub([mockBundle bundleIdentifier]).andReturn(kFakeBundleID);
  476. // Simulate missing app token error.
  477. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  478. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  479. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  480. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  481. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) {
  482. NSError *error = [NSError errorWithDomain:FIRAuthErrorDomain
  483. code:FIRAuthErrorCodeMissingAppToken
  484. userInfo:nil];
  485. callback(nil, error);
  486. });
  487. OCMExpect([_mockBackend getProjectConfig:[OCMArg any] callback:[OCMArg any]])
  488. .andCallBlock2(^(FIRGetProjectConfigRequest *request,
  489. FIRGetProjectConfigResponseCallback callback) {
  490. XCTAssertNotNil(request);
  491. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  492. id mockGetProjectConfigResponse = OCMClassMock([FIRGetProjectConfigResponse class]);
  493. OCMStub([mockGetProjectConfigResponse authorizedDomains]).
  494. andReturn(@[ kFakeAuthorizedDomain]);
  495. callback(mockGetProjectConfigResponse, nil);
  496. });
  497. });
  498. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  499. // Expect view controller presentation by UIDelegate.
  500. OCMExpect([_mockURLPresenter presentURL:OCMOCK_ANY
  501. UIDelegate:mockUIDelegate
  502. callbackMatcher:OCMOCK_ANY
  503. completion:OCMOCK_ANY]).andDo(^(NSInvocation *invocation) {
  504. __unsafe_unretained id unretainedArgument;
  505. // Indices 0 and 1 indicate the hidden arguments self and _cmd.
  506. // `completion` is at index 5
  507. [invocation getArgument:&unretainedArgument atIndex:5];
  508. FIRAuthURLPresentationCompletion completion = unretainedArgument;
  509. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  510. completion([NSURL URLWithString:kFakeRedirectURLStringUnknownError], nil);
  511. });
  512. });
  513. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  514. [_provider verifyPhoneNumber:kTestPhoneNumber
  515. UIDelegate:mockUIDelegate
  516. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  517. XCTAssertTrue([NSThread isMainThread]);
  518. XCTAssertEqual(error.code, FIRAuthErrorCodeAppVerificationUserInteractionFailure);
  519. XCTAssertNil(verificationID);
  520. [expectation fulfill];
  521. }];
  522. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  523. OCMVerifyAll(_mockBackend);
  524. OCMVerifyAll(_mockNotificationManager);
  525. }
  526. /** @fn testVerifyPhoneNumberUIDelegateRaiseException
  527. @brief Tests a invocation of @c verifyPhoneNumber:UIDelegate:completion: which results in an
  528. exception.
  529. */
  530. - (void)testVerifyPhoneNumberUIDelegateRaiseException {
  531. id mockBundle = OCMClassMock([NSBundle class]);
  532. OCMStub(ClassMethod([mockBundle mainBundle])).andReturn(mockBundle);
  533. OCMStub([mockBundle objectForInfoDictionaryKey:@"CFBundleURLTypes"])
  534. .andReturn(@[ @{ @"CFBundleURLSchemes" : @[ @"badscheme" ] } ]);
  535. id mockUIDelegate = OCMProtocolMock(@protocol(FIRAuthUIDelegate));
  536. XCTAssertThrows([_provider verifyPhoneNumber:kTestPhoneNumber
  537. UIDelegate:mockUIDelegate
  538. completion:^(NSString *_Nullable verificationID,
  539. NSError *_Nullable error) {
  540. XCTFail(@"Shouldn't call completion here.");
  541. }]);
  542. }
  543. /** @fn testNotForwardingNotification
  544. @brief Tests returning an error for the app failing to forward notification.
  545. */
  546. - (void)testNotForwardingNotification {
  547. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  548. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(NO); });
  549. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  550. [_provider verifyPhoneNumber:kTestPhoneNumber
  551. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  552. XCTAssertTrue([NSThread isMainThread]);
  553. XCTAssertNil(verificationID);
  554. XCTAssertEqual(error.code, FIRAuthErrorCodeNotificationNotForwarded);
  555. [expectation fulfill];
  556. }];
  557. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  558. OCMVerifyAll(_mockNotificationManager);
  559. }
  560. /** @fn testMissingAPNSToken
  561. @brief Tests returning an error for the app failing to provide an APNS device token.
  562. */
  563. - (void)testMissingAPNSToken {
  564. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  565. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  566. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  567. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  568. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(nil, nil); });
  569. // Expect verify client request to the backend wth empty token.
  570. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  571. .andCallBlock2(^(FIRVerifyClientRequest *request,
  572. FIRVerifyClientResponseCallback callback) {
  573. XCTAssertNil(request.appToken);
  574. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  575. // The backend is supposed to return an error.
  576. callback(nil, [NSError errorWithDomain:FIRAuthErrorDomain
  577. code:FIRAuthErrorCodeMissingAppToken
  578. userInfo:nil]);
  579. });
  580. });
  581. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  582. [_provider verifyPhoneNumber:kTestPhoneNumber
  583. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  584. XCTAssertTrue([NSThread isMainThread]);
  585. XCTAssertNil(verificationID);
  586. XCTAssertEqualObjects(error.domain, FIRAuthErrorDomain);
  587. XCTAssertEqual(error.code, FIRAuthErrorCodeMissingAppToken);
  588. [expectation fulfill];
  589. }];
  590. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  591. OCMVerifyAll(_mockNotificationManager);
  592. OCMVerifyAll(_mockAppCredentialManager);
  593. OCMVerifyAll(_mockAPNSTokenManager);
  594. }
  595. /** @fn testVerifyClient
  596. @brief Tests verifying client before sending verification code.
  597. */
  598. - (void)testVerifyClient {
  599. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  600. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  601. OCMExpect([_mockAppCredentialManager credential]).andReturn(nil);
  602. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  603. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  604. type:FIRAuthAPNSTokenTypeProd];
  605. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  606. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  607. // Expect verify client request to the backend.
  608. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  609. .andCallBlock2(^(FIRVerifyClientRequest *request,
  610. FIRVerifyClientResponseCallback callback) {
  611. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  612. XCTAssertFalse(request.isSandbox);
  613. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  614. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  615. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  616. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  617. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  618. callback(mockVerifyClientResponse, nil);
  619. });
  620. });
  621. // Mock receiving of push notification.
  622. OCMExpect([[_mockAppCredentialManager ignoringNonObjectArgs]
  623. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  624. .andCallIdDoubleIdBlock(^(NSString *receipt,
  625. NSTimeInterval timeout,
  626. FIRAuthAppCredentialCallback callback) {
  627. XCTAssertEqualObjects(receipt, kTestReceipt);
  628. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  629. // into the block either, so we can't verify it here.
  630. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  631. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  632. });
  633. });
  634. // Expect send verification code request to the backend.
  635. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  636. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  637. FIRSendVerificationCodeResponseCallback callback) {
  638. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  639. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  640. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  641. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  642. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  643. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  644. callback(mockSendVerificationCodeResponse, nil);
  645. });
  646. });
  647. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  648. [_provider verifyPhoneNumber:kTestPhoneNumber
  649. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  650. XCTAssertTrue([NSThread isMainThread]);
  651. XCTAssertNil(error);
  652. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  653. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  654. [expectation fulfill];
  655. }];
  656. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  657. OCMVerifyAll(_mockBackend);
  658. OCMVerifyAll(_mockNotificationManager);
  659. OCMVerifyAll(_mockAppCredentialManager);
  660. OCMVerifyAll(_mockAPNSTokenManager);
  661. }
  662. /** @fn testSendVerificationCodeFailedRetry
  663. @brief Tests failed retry after failing to send verification code.
  664. */
  665. - (void)testSendVerificationCodeFailedRetry {
  666. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  667. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  668. // Expect twice due to null check consumes one expectation.
  669. OCMExpect([_mockAppCredentialManager credential])
  670. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  671. secret:kTestOldSecret]);
  672. OCMExpect([_mockAppCredentialManager credential])
  673. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  674. secret:kTestOldSecret]);
  675. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  676. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  677. type:FIRAuthAPNSTokenTypeProd];
  678. // Expect first sendVerificationCode request to the backend, with request containing old app
  679. // credential.
  680. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  681. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  682. FIRSendVerificationCodeResponseCallback callback) {
  683. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  684. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  685. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  686. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  687. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  688. });
  689. });
  690. // Expect send verification code request to the backend, with request containing new app
  691. // credential data.
  692. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  693. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  694. FIRSendVerificationCodeResponseCallback callback) {
  695. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  696. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  697. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  698. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  699. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  700. });
  701. });
  702. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  703. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  704. // Expect verify client request to the backend.
  705. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  706. .andCallBlock2(^(FIRVerifyClientRequest *request,
  707. FIRVerifyClientResponseCallback callback) {
  708. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  709. XCTAssertFalse(request.isSandbox);
  710. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  711. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  712. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  713. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  714. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  715. callback(mockVerifyClientResponse, nil);
  716. });
  717. });
  718. // Mock receiving of push notification.
  719. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  720. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  721. .andCallIdDoubleIdBlock(^(NSString *receipt,
  722. NSTimeInterval timeout,
  723. FIRAuthAppCredentialCallback callback) {
  724. XCTAssertEqualObjects(receipt, kTestReceipt);
  725. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  726. // into the block either, so we can't verify it here.
  727. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  728. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  729. });
  730. });
  731. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  732. [_provider verifyPhoneNumber:kTestPhoneNumber
  733. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  734. XCTAssertTrue([NSThread isMainThread]);
  735. XCTAssertNil(verificationID);
  736. XCTAssertEqual(error.code, FIRAuthErrorCodeInternalError);
  737. [expectation fulfill];
  738. }];
  739. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  740. OCMVerifyAll(_mockBackend);
  741. OCMVerifyAll(_mockNotificationManager);
  742. OCMVerifyAll(_mockAppCredentialManager);
  743. OCMVerifyAll(_mockAPNSTokenManager);
  744. }
  745. /** @fn testSendVerificationCodeSuccessFulRetry
  746. @brief Tests successful retry after failing to send verification code.
  747. */
  748. - (void)testSendVerificationCodeSuccessFulRetry {
  749. OCMExpect([_mockNotificationManager checkNotificationForwardingWithCallback:OCMOCK_ANY])
  750. .andCallBlock1(^(FIRAuthNotificationForwardingCallback callback) { callback(YES); });
  751. // Expect twice due to null check consumes one expectation.
  752. OCMExpect([_mockAppCredentialManager credential])
  753. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  754. secret:kTestOldSecret]);
  755. OCMExpect([_mockAppCredentialManager credential])
  756. .andReturn([[FIRAuthAppCredential alloc] initWithReceipt:kTestOldReceipt
  757. secret:kTestOldSecret]);
  758. NSData *data = [@"!@#$%^" dataUsingEncoding:NSUTF8StringEncoding];
  759. FIRAuthAPNSToken *token = [[FIRAuthAPNSToken alloc] initWithData:data
  760. type:FIRAuthAPNSTokenTypeProd];
  761. // Expect first sendVerificationCode request to the backend, with request containing old app
  762. // credential.
  763. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  764. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  765. FIRSendVerificationCodeResponseCallback callback) {
  766. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  767. XCTAssertEqualObjects(request.appCredential.receipt, kTestOldReceipt);
  768. XCTAssertEqualObjects(request.appCredential.secret, kTestOldSecret);
  769. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  770. callback(nil, [FIRAuthErrorUtils invalidAppCredentialWithMessage:nil]);
  771. });
  772. });
  773. // Expect send verification code request to the backend, with request containing new app
  774. // credential data.
  775. OCMExpect([_mockBackend sendVerificationCode:[OCMArg any] callback:[OCMArg any]])
  776. .andCallBlock2(^(FIRSendVerificationCodeRequest *request,
  777. FIRSendVerificationCodeResponseCallback callback) {
  778. XCTAssertEqualObjects(request.phoneNumber, kTestPhoneNumber);
  779. XCTAssertEqualObjects(request.appCredential.receipt, kTestReceipt);
  780. XCTAssertEqualObjects(request.appCredential.secret, kTestSecret);
  781. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  782. id mockSendVerificationCodeResponse = OCMClassMock([FIRSendVerificationCodeResponse class]);
  783. OCMStub([mockSendVerificationCodeResponse verificationID]).andReturn(kTestVerificationID);
  784. callback(mockSendVerificationCodeResponse, nil);
  785. });
  786. });
  787. OCMExpect([_mockAPNSTokenManager getTokenWithCallback:OCMOCK_ANY])
  788. .andCallBlock1(^(FIRAuthAPNSTokenCallback callback) { callback(token, nil); });
  789. // Expect verify client request to the backend.
  790. OCMExpect([_mockBackend verifyClient:[OCMArg any] callback:[OCMArg any]])
  791. .andCallBlock2(^(FIRVerifyClientRequest *request,
  792. FIRVerifyClientResponseCallback callback) {
  793. XCTAssertEqualObjects(request.appToken, @"21402324255E");
  794. XCTAssertFalse(request.isSandbox);
  795. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  796. id mockVerifyClientResponse = OCMClassMock([FIRVerifyClientResponse class]);
  797. OCMStub([mockVerifyClientResponse receipt]).andReturn(kTestReceipt);
  798. OCMStub([mockVerifyClientResponse suggestedTimeOutDate])
  799. .andReturn([NSDate dateWithTimeIntervalSinceNow:kTestTimeout]);
  800. callback(mockVerifyClientResponse, nil);
  801. });
  802. });
  803. // Mock receiving of push notification.
  804. OCMStub([[_mockAppCredentialManager ignoringNonObjectArgs]
  805. didStartVerificationWithReceipt:OCMOCK_ANY timeout:0 callback:OCMOCK_ANY])
  806. .andCallIdDoubleIdBlock(^(NSString *receipt,
  807. NSTimeInterval timeout,
  808. FIRAuthAppCredentialCallback callback) {
  809. XCTAssertEqualObjects(receipt, kTestReceipt);
  810. // Unfortunately 'ignoringNonObjectArgs' means the real value for 'timeout' doesn't get passed
  811. // into the block either, so we can't verify it here.
  812. dispatch_async(FIRAuthGlobalWorkQueue(), ^() {
  813. callback([[FIRAuthAppCredential alloc] initWithReceipt:kTestReceipt secret:kTestSecret]);
  814. });
  815. });
  816. XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
  817. [_provider verifyPhoneNumber:kTestPhoneNumber
  818. completion:^(NSString *_Nullable verificationID, NSError *_Nullable error) {
  819. XCTAssertNil(error);
  820. XCTAssertEqualObjects(verificationID, kTestVerificationID);
  821. XCTAssertEqualObjects(verificationID.fir_authPhoneNumber, kTestPhoneNumber);
  822. [expectation fulfill];
  823. }];
  824. [self waitForExpectationsWithTimeout:kExpectationTimeout handler:nil];
  825. OCMVerifyAll(_mockBackend);
  826. OCMVerifyAll(_mockNotificationManager);
  827. OCMVerifyAll(_mockAppCredentialManager);
  828. OCMVerifyAll(_mockAPNSTokenManager);
  829. }
  830. #pragma clang diagnostic pop // ignored "-Wdeprecated-declarations"
  831. @end
  832. NS_ASSUME_NONNULL_END