RPCBaseTests.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. import XCTest
  16. @testable import FirebaseAuth
  17. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  18. class RPCBaseTests: XCTestCase {
  19. let kEmail = "user@company.com"
  20. let kFakePassword = "!@#$%^"
  21. let kDisplayName = "User Doe"
  22. let kLocalID = "testLocalId"
  23. let kFakeOobCode = "fakeOobCode"
  24. let kRefreshToken = "fakeRefreshToken"
  25. let kCustomToken = "CUSTOM_TOKEN"
  26. let kFakeEmailSignInLink = "https://test.app.goo.gl/?link=https://test.firebase" +
  27. "app.com/__/auth/action?apiKey%3DtestAPIKey%26mode%3DsignIn%26oobCode%3Dtestoobcode%26continueU" +
  28. "rl%3Dhttps://test.apps.com&ibi=com.test.com&ifl=https://test.firebaseapp.com/__/auth/" +
  29. "action?apiKey%3DtestAPIKey%26mode%3DsignIn%26oobCode%3Dtestoobcode%26continueUrl%3Dhttps://" +
  30. "test.apps.com"
  31. let kFakeEmailSignInDeeplink =
  32. "https://example.domain.com/?apiKey=testAPIKey&oobCode=testoobcode&mode=signIn"
  33. let kContinueURL = "continueURL"
  34. let kIosBundleID = "testBundleID"
  35. let kAndroidPackageName = "androidpackagename"
  36. let kAndroidMinimumVersion = "3.0"
  37. let kDynamicLinkDomain = "test.page.link"
  38. let kTestPhotoURL = "https://host.domain/image"
  39. let kCreationDateTimeIntervalInSeconds = 1_505_858_500.0
  40. let kLastSignInDateTimeIntervalInSeconds = 1_505_858_583.0
  41. let kTestPhoneNumber = "415-555-1234"
  42. static let kOAuthSessionID = "sessionID"
  43. static let kOAuthRequestURI = "requestURI"
  44. let kGoogleIDToken = "GOOGLE_ID_TOKEN"
  45. let kGoogleAccessToken = "GOOGLE_ACCESS_TOKEN"
  46. let kGoogleID = "GOOGLE_ID"
  47. let kGoogleEmail = "usergmail.com"
  48. let kGoogleDisplayName = "Google Doe"
  49. let kGoogleProfile = ["email": "usergmail.com", "given_name": "MyFirst", "family_name": "MyLast"]
  50. let kUserName = "User Doe"
  51. /** @var kTestAPIKey
  52. @brief Fake API key used for testing.
  53. */
  54. let kTestAPIKey = "APIKey"
  55. /** @var kTestFirebaseAppID
  56. @brief Fake Firebase app ID used for testing.
  57. */
  58. let kTestFirebaseAppID = "appID"
  59. /** @var kTestIdentifier
  60. @brief Fake identifier key used for testing.
  61. */
  62. let kTestIdentifier = "Identifier"
  63. var rpcIssuer: FakeBackendRPCIssuer?
  64. var rpcImplementation: AuthBackendImplementation?
  65. override func setUp() {
  66. rpcIssuer = FakeBackendRPCIssuer()
  67. AuthBackend.setDefaultBackendImplementationWithRPCIssuer(issuer: rpcIssuer)
  68. rpcImplementation = AuthBackend.implementation()
  69. }
  70. override func tearDown() {
  71. rpcIssuer = nil
  72. AuthBackend.setDefaultBackendImplementationWithRPCIssuer(issuer: nil)
  73. }
  74. /** @fn checkRequest
  75. @brief Tests the encoding of a request.
  76. */
  77. @discardableResult func checkRequest(request: AuthRPCRequest,
  78. expected: String,
  79. key: String,
  80. value: String?,
  81. checkPostBody: Bool = false) throws -> FakeBackendRPCIssuer {
  82. AuthBackend.post(withRequest: request) { response, error in
  83. XCTFail("No explicit response from the fake backend.")
  84. }
  85. let rpcIssuer = try XCTUnwrap(rpcIssuer)
  86. XCTAssertEqual(rpcIssuer.requestURL?.absoluteString, expected)
  87. if checkPostBody,
  88. let containsPostBody = request.containsPostBody?() {
  89. XCTAssertFalse(containsPostBody)
  90. } else if let requestDictionary = rpcIssuer.decodedRequest as? [String: AnyHashable] {
  91. XCTAssertEqual(requestDictionary[key], value)
  92. } else {
  93. XCTFail("decodedRequest is not a dictionary")
  94. }
  95. return rpcIssuer
  96. }
  97. /** @fn checkBackendError
  98. @brief This test checks error messagess from the backend map to the expected error codes
  99. */
  100. func checkBackendError(request: AuthRPCRequest,
  101. message: String = "",
  102. reason: String? = nil,
  103. json: [String: AnyHashable]? = nil,
  104. errorCode: AuthErrorCode,
  105. errorReason: String? = nil,
  106. underlyingErrorKey: String? = nil,
  107. checkLocalizedDescription: String? = nil) throws {
  108. var callbackInvoked = false
  109. var rpcResponse: CreateAuthURIResponse?
  110. var rpcError: NSError?
  111. AuthBackend.post(withRequest: request) { response, error in
  112. callbackInvoked = true
  113. rpcResponse = response as? CreateAuthURIResponse
  114. rpcError = error as? NSError
  115. }
  116. if let json = json {
  117. _ = try rpcIssuer?.respond(withJSON: json)
  118. } else if let reason = reason {
  119. _ = try rpcIssuer?.respond(underlyingErrorMessage: reason, message: message)
  120. } else {
  121. _ = try rpcIssuer?.respond(serverErrorMessage: message)
  122. }
  123. XCTAssert(callbackInvoked)
  124. XCTAssertNil(rpcResponse)
  125. XCTAssertEqual(rpcError?.code, errorCode.rawValue)
  126. if errorCode == .internalError {
  127. let underlyingError = try XCTUnwrap(rpcError?.userInfo[NSUnderlyingErrorKey] as? NSError)
  128. XCTAssertNotNil(underlyingError.userInfo[AuthErrorUtils.userInfoDeserializedResponseKey])
  129. }
  130. if let errorReason {
  131. XCTAssertEqual(errorReason, rpcError?.userInfo[NSLocalizedFailureReasonErrorKey] as? String)
  132. }
  133. if let checkLocalizedDescription {
  134. let localizedDescription = try XCTUnwrap(rpcError?
  135. .userInfo[NSLocalizedDescriptionKey] as? String)
  136. XCTAssertEqual(checkLocalizedDescription, localizedDescription)
  137. }
  138. }
  139. func makeRequestConfiguration() -> AuthRequestConfiguration {
  140. return AuthRequestConfiguration(
  141. apiKey: kTestAPIKey,
  142. appID: kTestFirebaseAppID
  143. )
  144. }
  145. static let kFakeAccessToken =
  146. "eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZDBhODBiYzh" +
  147. "jM2U5NDM0OTc0ZDFmMWRiNTEifQ." +
  148. "eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZmItc2EtdXBncm" +
  149. "FkZWQiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIs" +
  150. "InN1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1MjIzNjQxNTcsImVtYWlsIjoiYXVuaXRlc3R1c2" +
  151. "VyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6" +
  152. "WyJhdW5pdGVzdHVzZXJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0=." +
  153. "WFQqSrpVnxx7m" +
  154. "UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_" +
  155. "BKkcbZuq8O7iDVpOvqKC" +
  156. "3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-" +
  157. "XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN" +
  158. "hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-" +
  159. "dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW" +
  160. "lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA"
  161. static let kFakeAccessTokenLength415 =
  162. "eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD" +
  163. "BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ." +
  164. "eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd" +
  165. "CIsImF1ZCI6InRlc3RfYXVkIiwiYXV0aF90aW1lIjoxNTIyMzYwNTQ5LCJ1c2VyX2lkIjoidGVzdF91c2VyX2lkIiwic3V" +
  166. "iIjoidGVzdF9zdWIiLCJpYXQiOjE1MjIzNjA1NTcsImV4cCI6MTUyMjM2NDE1NywiZW1haWwiOiJhdW5pdGVzdHVzZXJAZ" +
  167. "21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImF" +
  168. "1bml0ZXN0dXNlckBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ=.WFQqSrpVnxx7m" +
  169. "UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_" +
  170. "BKkcbZuq8O7iDVpOvqKC" +
  171. "3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-" +
  172. "XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN" +
  173. "hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-" +
  174. "dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW" +
  175. "lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA"
  176. static let kFakeAccessTokenLength416 =
  177. "eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD" +
  178. "BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ." +
  179. "eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd" +
  180. "DIiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIsInN" +
  181. "1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1MjIzNjQxNTcsImVtYWlsIjoiYXVuaXRlc3R1c2VyQ" +
  182. "GdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJ" +
  183. "hdW5pdGVzdHVzZXJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0=.WFQqSrpVnxx7m" +
  184. "UrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_" +
  185. "BKkcbZuq8O7iDVpOvqKC" +
  186. "3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-" +
  187. "XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjN" +
  188. "hHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-" +
  189. "dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbW" +
  190. "lddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA"
  191. static let kFakeAccessTokenLength523 =
  192. "eyJhbGciOimnuzI1NiIsImtpZCI6ImY1YjE4Mjc2YTQ4NjYxZD" +
  193. "BhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ." +
  194. "eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vdGVzd" +
  195. "DQiLCJhdWQiOiJ0ZXN0X2F1ZCIsImF1dGhfdGltZSI6MTUyMjM2MDU0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZF81NDM" +
  196. "yIiwic3ViIjoidGVzdF9zdWIiLCJpYXQiOjE1MjIzNjA1NTcsImV4cCI6MTUyMjM2NDE1OSwiZW1haWwiOiJhdW5pdGVzd" +
  197. "HVzZXI0QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWl" +
  198. "sIjpbImF1bml0ZXN0dXNlckBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ=." +
  199. "WFQqSrpVn" +
  200. "xx7mUrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-aTYMvND3-4aS3qRnN2kvk9KJAaF3eI_" +
  201. "BKkcbZuq8O7iDVpO" +
  202. "vqKC3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-" +
  203. "XtBt1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH" +
  204. "7jjNhHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-" +
  205. "dDV7jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqV" +
  206. "kPbWlddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA"
  207. static let kFakeAccessTokenWithBase64 =
  208. "ey?hbGciOimnuzI1NiIsImtpZCI6ImY1YjE4M" +
  209. "jc2YTQ4NjYxZDBhODBiYzhjM2U5NDM0OTc0ZDFmMWRiNTEifQ." +
  210. "eyJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2ds" +
  211. "ZS5jb20vZmItc2EtdXBncmFkZWQiLCJhdWQiOiI_Pz8_Pz8_Pz8_Pj4-Pj4-Pj4-" +
  212. "PiIsImF1dGhfdGltZSI6MTUyMjM2MD" +
  213. "U0OSwidXNlcl9pZCI6InRlc3RfdXNlcl9pZCIsInN1YiI6InRlc3Rfc3ViIiwiaWF0IjoxNTIyMzYwNTU3LCJleHAiOjE1" +
  214. "MjIzNjQxNTcsImVtYWlsIjoiPj4-Pj4-Pj4_Pz8_Pz8_" +
  215. "P0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm" +
  216. "ZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsiYXVuaXRlc3R1c2VyQGdtYWlsLmNvbSJdfSwic2lnbl9pbl9w" +
  217. "cm92aWRlciI6IlBhc3N3b3JkIn19.WFQqSrpVnxx7mUrdKZA517Sp4ZBt-l2xQzGKNMVE90JB3vuNa-NyWZC-" +
  218. "aTYMvND3-" +
  219. "4aS3qRnN2kvk9KJAaF3eI_BKkcbZuq8O7iDVpOvqKC3QcW0PnwqSPChL3XqoDF322FcBEgemwwgaEVZMuo7GhJvHw-" +
  220. "XtBt" +
  221. "1KRXOoGHcr3P6RsvoulUouKQmqt6TP27eZtrgH7jjNhHm7gjX_WaRmgTOvYsuDbBBGdE15yIVZ3acI4cFUgwMRhaW-" +
  222. "dDV7" +
  223. "jTOqZGYJlTsI5oRMehphoVnYnEedJga28r4mqVkPbWlddL4dVVm85FYmQcRc0b2CLMnSevBDlwu754ZUZmRgnuvDA"
  224. func setFakeSecureTokenService(fakeAccessToken: String = RPCBaseTests.kFakeAccessToken) {
  225. rpcIssuer?.fakeSecureTokenServiceJSON = ["access_token": fakeAccessToken,
  226. "expires_in": "3600"]
  227. }
  228. func setFakeGetAccountProvider(withNewDisplayName displayName: String = "User Doe",
  229. withLocalID localID: String = "testLocalId",
  230. withProviderID providerID: String = "testProviderID",
  231. withFederatedID federatedID: String = "testFederatedId",
  232. withEmail email: String = "user@company.com",
  233. withPasswordHash passwordHash: String? = nil) {
  234. let kProviderUserInfoKey = "providerUserInfo"
  235. let kPhotoUrlKey = "photoUrl"
  236. let kProviderIDkey = "providerId"
  237. let kDisplayNameKey = "displayName"
  238. let kFederatedIDKey = "federatedId"
  239. let kEmailKey = "email"
  240. let kPasswordHashKey = "passwordHash"
  241. let kTestPasswordHash = passwordHash
  242. let kEmailVerifiedKey = "emailVerified"
  243. let kLocalIDKey = "localId"
  244. rpcIssuer?.fakeGetAccountProviderJSON = [[
  245. kProviderUserInfoKey: [[
  246. kProviderIDkey: providerID,
  247. kDisplayNameKey: displayName,
  248. kPhotoUrlKey: kTestPhotoURL,
  249. kFederatedIDKey: federatedID,
  250. kEmailKey: email,
  251. ]],
  252. kLocalIDKey: localID,
  253. kDisplayNameKey: displayName,
  254. kEmailKey: email,
  255. kPhotoUrlKey: kTestPhotoURL,
  256. kEmailVerifiedKey: true,
  257. kPasswordHashKey: kTestPasswordHash,
  258. "phoneNumber": kTestPhoneNumber,
  259. ]]
  260. }
  261. func setFakeGoogleGetAccountProvider() {
  262. setFakeGetAccountProvider(withNewDisplayName: kGoogleDisplayName,
  263. withProviderID: GoogleAuthProvider.id,
  264. withFederatedID: kGoogleID,
  265. withEmail: kGoogleEmail)
  266. }
  267. func setFakeGetAccountProviderAnonymous() {
  268. let kPasswordHashKey = "passwordHash"
  269. let kTestPasswordHash = "testPasswordHash"
  270. let kLocalIDKey = "localId"
  271. rpcIssuer?.fakeGetAccountProviderJSON = [[
  272. kLocalIDKey: kLocalID,
  273. kPasswordHashKey: kTestPasswordHash,
  274. ]]
  275. }
  276. func createGroup() -> DispatchGroup {
  277. let group = DispatchGroup()
  278. rpcIssuer?.group = group
  279. group.enter()
  280. return group
  281. }
  282. func fakeActionCodeSettings() -> ActionCodeSettings {
  283. let settings = ActionCodeSettings()
  284. settings.iOSBundleID = kIosBundleID
  285. settings.setAndroidPackageName(kAndroidPackageName,
  286. installIfNotAvailable: true,
  287. minimumVersion: kAndroidMinimumVersion)
  288. settings.handleCodeInApp = true
  289. settings.url = URL(string: kContinueURL)
  290. settings.dynamicLinkDomain = kDynamicLinkDomain
  291. return settings
  292. }
  293. func assertUserGoogle(_ user: User?) throws {
  294. let user = try XCTUnwrap(user)
  295. XCTAssertEqual(user.uid, kLocalID)
  296. XCTAssertEqual(user.displayName, kGoogleDisplayName)
  297. XCTAssertEqual(user.providerData.count, 1)
  298. let googleUserInfo = user.providerData[0]
  299. XCTAssertEqual(googleUserInfo.providerID, GoogleAuthProvider.id)
  300. XCTAssertEqual(googleUserInfo.uid, kGoogleID)
  301. XCTAssertEqual(googleUserInfo.displayName, kGoogleDisplayName)
  302. XCTAssertEqual(googleUserInfo.email, kGoogleEmail)
  303. }
  304. }