FIRVerifyPasswordResponseTests.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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 <XCTest/XCTest.h>
  17. #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRAuthErrors.h"
  18. #import "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
  19. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordRequest.h"
  20. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordResponse.h"
  21. #import "FirebaseAuth/Tests/Unit/FIRFakeBackendRPCIssuer.h"
  22. /** @var kTestPassword
  23. @brief Testing user password.
  24. */
  25. static NSString *const kTestPassword = @"testpassword";
  26. /** @var kTestAPIKey
  27. @brief Fake API key used for testing.
  28. */
  29. static NSString *const kTestAPIKey = @"_test_API_key_";
  30. /** @var kTestFirebaseAppID
  31. @brief Fake Firebase app ID used for testing.
  32. */
  33. static NSString *const kTestFirebaseAppID = @"appID";
  34. /** @var kLocalIDKey
  35. @brief The name of the 'localID' property in the response.
  36. */
  37. static NSString *const kLocalIDKey = @"localId";
  38. /** @var kTestLocalID
  39. @brief The fake localID for testing the response.
  40. */
  41. static NSString *const kTestLocalID = @"testLocalId";
  42. /** @var kEmailKey
  43. @brief The name of the 'email' property in the response.
  44. */
  45. static NSString *const kEmailKey = @"email";
  46. /** @var kTestEmail
  47. @brief Fake user email for testing the response.
  48. */
  49. static NSString *const kTestEmail = @"test@gmail.com";
  50. /** @var kDisplayNameKey
  51. @brief The name of the 'displayName' property in the response.
  52. */
  53. static NSString *const kDisplayNameKey = @"displayName";
  54. /** @var kTestDisplayName
  55. @brief Fake displayName for testing the response.
  56. */
  57. static NSString *const kTestDisplayName = @"testDisplayName";
  58. /** @var kIDTokenKey
  59. @brief The name of the "IDToken" property in the response.
  60. */
  61. static NSString *const kIDTokenKey = @"idToken";
  62. /** @var kTestIDToken
  63. @brief Testing ID token for verifying assertion.
  64. */
  65. static NSString *const kTestIDToken = @"ID_TOKEN";
  66. /** @var kExpiresInKey
  67. @brief The name of the "expiresIn" property in the response.
  68. */
  69. static NSString *const kExpiresInKey = @"expiresIn";
  70. /** @var kTestExpiresIn
  71. @brief Fake token expiration time.
  72. */
  73. static NSString *const kTestExpiresIn = @"12345";
  74. /** @var kRefreshTokenKey
  75. @brief The name of the "refreshToken" property in the response.
  76. */
  77. static NSString *const kRefreshTokenKey = @"refreshToken";
  78. /** @var kTestRefreshToken
  79. @brief Fake refresh token.
  80. */
  81. static NSString *const kTestRefreshToken = @"REFRESH_TOKEN";
  82. /** @var kOperationNotAllowedErrorMessage
  83. @brief This is the error message the server will respond with if Admin disables IDP specified by
  84. provider.
  85. */
  86. static NSString *const kOperationNotAllowedErrorMessage = @"OPERATION_NOT_ALLOWED";
  87. /** @var kPasswordLoginDisabledErrorMessage
  88. @brief This is the error message the server responds with if password login is disabled.
  89. */
  90. static NSString *const kPasswordLoginDisabledErrorMessage = @"PASSWORD_LOGIN_DISABLED";
  91. /** @var kPhotoUrlKey
  92. @brief The name of the 'photoUrl' property in the response.
  93. */
  94. static NSString *const kPhotoUrlKey = @"photoUrl";
  95. /** @var kTestPhotoUrl
  96. @brief Fake photoUrl for testing the response.
  97. */
  98. static NSString *const kTestPhotoUrl = @"www.example.com";
  99. /** @var kUserDisabledErrorMessage
  100. @brief This is the error message the server will respond with if the user's account has been
  101. disabled.
  102. */
  103. static NSString *const kUserDisabledErrorMessage = @"USER_DISABLED";
  104. /** @var kEmailNotFoundErrorMessage
  105. @brief This is the error message the server will respond with if the email entered is not
  106. found.
  107. */
  108. static NSString *const kEmailNotFoundErrorMessage = @"EMAIL_NOT_FOUND";
  109. /** @var kWrongPasswordErrorMessage
  110. @brief This is the error message the server will respond with if the user entered a wrong
  111. password.
  112. */
  113. static NSString *const kWrongPasswordErrorMessage = @"INVALID_PASSWORD";
  114. /** @var kInvalidEmailErrorMessage
  115. @brief The error returned by the server if the email is invalid.
  116. */
  117. static NSString *const kInvalidEmailErrorMessage = @"INVALID_EMAIL";
  118. /** @var kBadRequestErrorMessage
  119. @brief This is the error message returned when a bad request is made; often due to a bad API
  120. Key.
  121. */
  122. static NSString *const kBadRequestErrorMessage = @"Bad Request";
  123. /** @var kInvalidKeyReasonValue
  124. @brief The value for the "reason" key indicating an invalid API Key was received by the server.
  125. */
  126. static NSString *const kInvalidKeyReasonValue = @"keyInvalid";
  127. /** @var kAppNotAuthorizedReasonValue
  128. @brief The value for the "reason" key indicating the App is not authorized to use Firebase
  129. Authentication.
  130. */
  131. static NSString *const kAppNotAuthorizedReasonValue = @"ipRefererBlocked";
  132. /** @var kTooManyAttemptsErrorMessage
  133. @brief This is the error message the server will respond with if a user has tried (and failed)
  134. to sign in too many times.
  135. */
  136. static NSString *const kTooManyAttemptsErrorMessage = @"TOO_MANY_ATTEMPTS_TRY_LATER:";
  137. /** @var kAllowedTimeDifference
  138. @brief Allowed difference when comparing times because of execution time and floating point
  139. error.
  140. */
  141. static const double kAllowedTimeDifference = 0.1;
  142. /** @class FIRVerifyPasswordResponseTests
  143. @brief Tests for @c FIRVerifyPasswordResponse.
  144. */
  145. @interface FIRVerifyPasswordResponseTests : XCTestCase
  146. @end
  147. @implementation FIRVerifyPasswordResponseTests {
  148. /** @var _RPCIssuer
  149. @brief This backend RPC issuer is used to fake network responses for each test in the suite.
  150. In the @c setUp method we initialize this and set @c FIRAuthBackend's RPC issuer to it.
  151. */
  152. FIRFakeBackendRPCIssuer *_RPCIssuer;
  153. /** @var _requestConfiguration
  154. @brief This is the request configuration used for testing.
  155. */
  156. FIRAuthRequestConfiguration *_requestConfiguration;
  157. }
  158. - (void)setUp {
  159. [super setUp];
  160. FIRFakeBackendRPCIssuer *RPCIssuer = [[FIRFakeBackendRPCIssuer alloc] init];
  161. [FIRAuthBackend setDefaultBackendImplementationWithRPCIssuer:RPCIssuer];
  162. _RPCIssuer = RPCIssuer;
  163. _requestConfiguration = [[FIRAuthRequestConfiguration alloc] initWithAPIKey:kTestAPIKey
  164. appID:kTestFirebaseAppID];
  165. }
  166. /** @fn testUserDisabledError
  167. @brief Tests that @c FIRAuthErrorCodeUserDisabled error is received if the email is disabled.
  168. */
  169. - (void)testUserDisabledError {
  170. FIRVerifyPasswordRequest *request =
  171. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  172. password:kTestPassword
  173. requestConfiguration:_requestConfiguration];
  174. __block BOOL callbackInvoked;
  175. __block FIRVerifyPasswordResponse *RPCResponse;
  176. __block NSError *RPCError;
  177. [FIRAuthBackend
  178. verifyPassword:request
  179. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  180. RPCResponse = response;
  181. RPCError = error;
  182. callbackInvoked = YES;
  183. }];
  184. [_RPCIssuer respondWithServerErrorMessage:kUserDisabledErrorMessage];
  185. XCTAssert(callbackInvoked);
  186. XCTAssertNil(RPCResponse);
  187. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeUserDisabled);
  188. }
  189. /** @fn testEmailNotFoundError
  190. @brief Tests that @c FIRAuthErrorCodeEmailNotFound error is received if the email is not found.
  191. */
  192. - (void)testEmailNotFoundError {
  193. FIRVerifyPasswordRequest *request =
  194. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  195. password:kTestPassword
  196. requestConfiguration:_requestConfiguration];
  197. __block BOOL callbackInvoked;
  198. __block FIRVerifyPasswordResponse *RPCResponse;
  199. __block NSError *RPCError;
  200. [FIRAuthBackend
  201. verifyPassword:request
  202. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  203. RPCResponse = response;
  204. RPCError = error;
  205. callbackInvoked = YES;
  206. }];
  207. [_RPCIssuer respondWithServerErrorMessage:kEmailNotFoundErrorMessage];
  208. XCTAssert(callbackInvoked);
  209. XCTAssertNil(RPCResponse);
  210. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeUserNotFound);
  211. }
  212. /** @fn testInvalidPasswordError
  213. @brief Tests that @c FIRAuthErrorCodeInvalidPassword error is received if the password is
  214. invalid.
  215. */
  216. - (void)testInvalidPasswordError {
  217. FIRVerifyPasswordRequest *request =
  218. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  219. password:kTestPassword
  220. requestConfiguration:_requestConfiguration];
  221. __block BOOL callbackInvoked;
  222. __block FIRVerifyPasswordResponse *RPCResponse;
  223. __block NSError *RPCError;
  224. [FIRAuthBackend
  225. verifyPassword:request
  226. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  227. RPCResponse = response;
  228. RPCError = error;
  229. callbackInvoked = YES;
  230. }];
  231. [_RPCIssuer respondWithServerErrorMessage:kWrongPasswordErrorMessage];
  232. XCTAssert(callbackInvoked);
  233. XCTAssertNil(RPCResponse);
  234. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeWrongPassword);
  235. }
  236. /** @fn testInvalidEmailError
  237. @brief Tests that @c FIRAuthErrorCodeInvalidEmail error is received if the email address has an
  238. incorrect format.
  239. */
  240. - (void)testInvalidEmailError {
  241. FIRVerifyPasswordRequest *request =
  242. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  243. password:kTestPassword
  244. requestConfiguration:_requestConfiguration];
  245. __block BOOL callbackInvoked;
  246. __block FIRVerifyPasswordResponse *RPCResponse;
  247. __block NSError *RPCError;
  248. [FIRAuthBackend
  249. verifyPassword:request
  250. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  251. RPCResponse = response;
  252. RPCError = error;
  253. callbackInvoked = YES;
  254. }];
  255. [_RPCIssuer respondWithServerErrorMessage:kInvalidEmailErrorMessage];
  256. XCTAssert(callbackInvoked);
  257. XCTAssertNil(RPCResponse);
  258. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeInvalidEmail);
  259. }
  260. /** @fn testTooManyAttemptsError
  261. @brief Tests that @c FIRAuthErrorCodeTooManyRequests error is received if too many sign-in
  262. attempts were made.
  263. */
  264. - (void)testTooManySignInAttemptsError {
  265. FIRVerifyPasswordRequest *request =
  266. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  267. password:kTestPassword
  268. requestConfiguration:_requestConfiguration];
  269. __block BOOL callbackInvoked;
  270. __block FIRVerifyPasswordResponse *RPCResponse;
  271. __block NSError *RPCError;
  272. [FIRAuthBackend
  273. verifyPassword:request
  274. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  275. RPCResponse = response;
  276. RPCError = error;
  277. callbackInvoked = YES;
  278. }];
  279. [_RPCIssuer respondWithServerErrorMessage:kTooManyAttemptsErrorMessage];
  280. XCTAssert(callbackInvoked);
  281. XCTAssertNil(RPCResponse);
  282. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeTooManyRequests);
  283. }
  284. /** @fn testKeyInvalid
  285. @brief Tests that @c FIRAuthErrorCodeInvalidApiKey error is received from the server.
  286. */
  287. - (void)testKeyInvalid {
  288. FIRVerifyPasswordRequest *request =
  289. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  290. password:kTestPassword
  291. requestConfiguration:_requestConfiguration];
  292. __block BOOL callbackInvoked;
  293. __block FIRVerifyPasswordResponse *RPCResponse;
  294. __block NSError *RPCError;
  295. [FIRAuthBackend
  296. verifyPassword:request
  297. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  298. RPCResponse = response;
  299. RPCError = error;
  300. callbackInvoked = YES;
  301. }];
  302. NSDictionary *errorDictionary = @{
  303. @"error" : @{
  304. @"message" : kBadRequestErrorMessage,
  305. @"errors" : @[ @{@"reason" : kInvalidKeyReasonValue} ]
  306. }
  307. };
  308. [_RPCIssuer respondWithJSONError:errorDictionary];
  309. XCTAssert(callbackInvoked);
  310. XCTAssertNil(RPCResponse);
  311. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeInvalidAPIKey);
  312. }
  313. /** @fn testOperationNotAllowedError
  314. @brief This test simulates a @c FIRAuthErrorCodeOperationNotAllowed error.
  315. */
  316. - (void)testOperationNotAllowedError {
  317. FIRVerifyPasswordRequest *request =
  318. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  319. password:kTestPassword
  320. requestConfiguration:_requestConfiguration];
  321. __block BOOL callbackInvoked;
  322. __block FIRVerifyPasswordResponse *RPCResponse;
  323. __block NSError *RPCError;
  324. [FIRAuthBackend
  325. verifyPassword:request
  326. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  327. callbackInvoked = YES;
  328. RPCResponse = response;
  329. RPCError = error;
  330. }];
  331. [_RPCIssuer respondWithServerErrorMessage:kOperationNotAllowedErrorMessage];
  332. XCTAssert(callbackInvoked);
  333. XCTAssertNotNil(RPCError);
  334. XCTAssertNil(RPCResponse);
  335. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeOperationNotAllowed);
  336. }
  337. /** @fn testPasswordLoginDisabledError
  338. @brief This test simulates a @c FIRAuthErrorCodeOperationNotAllowed error.
  339. */
  340. - (void)testPasswordLoginDisabledError {
  341. FIRVerifyPasswordRequest *request =
  342. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  343. password:kTestPassword
  344. requestConfiguration:_requestConfiguration];
  345. __block BOOL callbackInvoked;
  346. __block FIRVerifyPasswordResponse *RPCResponse;
  347. __block NSError *RPCError;
  348. [FIRAuthBackend
  349. verifyPassword:request
  350. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  351. callbackInvoked = YES;
  352. RPCResponse = response;
  353. RPCError = error;
  354. }];
  355. [_RPCIssuer respondWithServerErrorMessage:kPasswordLoginDisabledErrorMessage];
  356. XCTAssert(callbackInvoked);
  357. XCTAssertNotNil(RPCError);
  358. XCTAssertNil(RPCResponse);
  359. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeOperationNotAllowed);
  360. }
  361. /** @fn testAppNotAuthorized
  362. @brief Tests that @c FIRAuthErrorCodeAppNotAuthorized error is received from the server.
  363. */
  364. - (void)testAppNotAuthorized {
  365. FIRVerifyPasswordRequest *request =
  366. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  367. password:kTestPassword
  368. requestConfiguration:_requestConfiguration];
  369. __block BOOL callbackInvoked;
  370. __block FIRVerifyPasswordResponse *RPCResponse;
  371. __block NSError *RPCError;
  372. [FIRAuthBackend
  373. verifyPassword:request
  374. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  375. RPCResponse = response;
  376. RPCError = error;
  377. callbackInvoked = YES;
  378. }];
  379. NSDictionary *errorDictionary = @{
  380. @"error" : @{
  381. @"message" : kBadRequestErrorMessage,
  382. @"errors" : @[ @{@"reason" : kAppNotAuthorizedReasonValue} ]
  383. }
  384. };
  385. [_RPCIssuer respondWithJSONError:errorDictionary];
  386. XCTAssert(callbackInvoked);
  387. XCTAssertNil(RPCResponse);
  388. XCTAssertEqual(RPCError.code, FIRAuthErrorCodeAppNotAuthorized);
  389. }
  390. /** @fn testSuccessfulVerifyPasswordResponse
  391. @brief Tests a succesful attempt of the verify password flow.
  392. */
  393. - (void)testSuccessfulVerifyPasswordResponse {
  394. FIRVerifyPasswordRequest *request =
  395. [[FIRVerifyPasswordRequest alloc] initWithEmail:kTestEmail
  396. password:kTestPassword
  397. requestConfiguration:_requestConfiguration];
  398. __block BOOL callbackInvoked;
  399. __block FIRVerifyPasswordResponse *RPCResponse;
  400. __block NSError *RPCError;
  401. [FIRAuthBackend
  402. verifyPassword:request
  403. callback:^(FIRVerifyPasswordResponse *_Nullable response, NSError *_Nullable error) {
  404. RPCResponse = response;
  405. RPCError = error;
  406. callbackInvoked = YES;
  407. }];
  408. [_RPCIssuer respondWithJSON:@{
  409. kLocalIDKey : kTestLocalID,
  410. kEmailKey : kTestEmail,
  411. kDisplayNameKey : kTestDisplayName,
  412. kIDTokenKey : kTestIDToken,
  413. kExpiresInKey : kTestExpiresIn,
  414. kRefreshTokenKey : kTestRefreshToken,
  415. kPhotoUrlKey : kTestPhotoUrl
  416. }];
  417. XCTAssert(callbackInvoked);
  418. XCTAssertNotNil(RPCResponse);
  419. XCTAssertEqualObjects(RPCResponse.email, kTestEmail);
  420. XCTAssertEqualObjects(RPCResponse.localID, kTestLocalID);
  421. XCTAssertEqualObjects(RPCResponse.displayName, kTestDisplayName);
  422. XCTAssertEqualObjects(RPCResponse.IDToken, kTestIDToken);
  423. NSTimeInterval expiresIn = [RPCResponse.approximateExpirationDate timeIntervalSinceNow];
  424. XCTAssertEqualWithAccuracy(expiresIn, [kTestExpiresIn doubleValue], kAllowedTimeDifference);
  425. XCTAssertEqualObjects(RPCResponse.refreshToken, kTestRefreshToken);
  426. XCTAssertEqualObjects(RPCResponse.photoURL.absoluteString, kTestPhotoUrl);
  427. }
  428. @end