FIRAuthBackend.m 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539
  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 "FirebaseAuth/Sources/Backend/FIRAuthBackend.h"
  17. #if SWIFT_PACKAGE
  18. @import GTMSessionFetcherCore;
  19. #else
  20. #import <GTMSessionFetcher/GTMSessionFetcher.h>
  21. #import <GTMSessionFetcher/GTMSessionFetcherService.h>
  22. #endif
  23. #import "FirebaseAuth/Sources/Public/FirebaseAuth/FirebaseAuth.h"
  24. #import "FirebaseAppCheck/Interop/FIRAppCheckInterop.h"
  25. #import "FirebaseAppCheck/Interop/FIRAppCheckTokenResultInterop.h"
  26. #import "FirebaseAuth/Sources/Auth/FIRAuthGlobalWorkQueue.h"
  27. #import "FirebaseAuth/Sources/Auth/FIRAuth_Internal.h"
  28. #import "FirebaseAuth/Sources/AuthProvider/OAuth/FIROAuthCredential_Internal.h"
  29. #import "FirebaseAuth/Sources/Backend/RPC/FIRCreateAuthURIRequest.h"
  30. #import "FirebaseAuth/Sources/Backend/RPC/FIRCreateAuthURIResponse.h"
  31. #import "FirebaseAuth/Sources/Backend/RPC/FIRDeleteAccountRequest.h"
  32. #import "FirebaseAuth/Sources/Backend/RPC/FIRDeleteAccountResponse.h"
  33. #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInRequest.h"
  34. #import "FirebaseAuth/Sources/Backend/RPC/FIREmailLinkSignInResponse.h"
  35. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoRequest.h"
  36. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetAccountInfoResponse.h"
  37. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeRequest.h"
  38. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetOOBConfirmationCodeResponse.h"
  39. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetProjectConfigRequest.h"
  40. #import "FirebaseAuth/Sources/Backend/RPC/FIRGetProjectConfigResponse.h"
  41. #import "FirebaseAuth/Sources/Backend/RPC/FIRResetPasswordRequest.h"
  42. #import "FirebaseAuth/Sources/Backend/RPC/FIRResetPasswordResponse.h"
  43. #import "FirebaseAuth/Sources/Backend/RPC/FIRRevokeTokenRequest.h"
  44. #import "FirebaseAuth/Sources/Backend/RPC/FIRRevokeTokenResponse.h"
  45. #import "FirebaseAuth/Sources/Backend/RPC/FIRSecureTokenRequest.h"
  46. #import "FirebaseAuth/Sources/Backend/RPC/FIRSecureTokenResponse.h"
  47. #import "FirebaseAuth/Sources/Backend/RPC/FIRSendVerificationCodeRequest.h"
  48. #import "FirebaseAuth/Sources/Backend/RPC/FIRSendVerificationCodeResponse.h"
  49. #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoRequest.h"
  50. #import "FirebaseAuth/Sources/Backend/RPC/FIRSetAccountInfoResponse.h"
  51. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterRequest.h"
  52. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignInWithGameCenterResponse.h"
  53. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserRequest.h"
  54. #import "FirebaseAuth/Sources/Backend/RPC/FIRSignUpNewUserResponse.h"
  55. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionRequest.h"
  56. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyAssertionResponse.h"
  57. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyClientRequest.h"
  58. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyClientResponse.h"
  59. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenRequest.h"
  60. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyCustomTokenResponse.h"
  61. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordRequest.h"
  62. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPasswordResponse.h"
  63. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberRequest.h"
  64. #import "FirebaseAuth/Sources/Backend/RPC/FIRVerifyPhoneNumberResponse.h"
  65. #import "FirebaseAuth/Sources/Utilities/FIRAuthErrorUtils.h"
  66. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  67. #if TARGET_OS_IOS
  68. #import "FirebaseAuth/Sources/Public/FirebaseAuth/FIRPhoneAuthProvider.h"
  69. #import "FirebaseAuth/Sources/AuthProvider/Phone/FIRPhoneAuthCredential_Internal.h"
  70. #import "FirebaseAuth/Sources/MultiFactor/Phone/FIRPhoneMultiFactorInfo+Internal.h"
  71. #endif
  72. NS_ASSUME_NONNULL_BEGIN
  73. /** @var kClientVersionHeader
  74. @brief HTTP header name for the client version.
  75. */
  76. static NSString *const kClientVersionHeader = @"X-Client-Version";
  77. /** @var kIosBundleIdentifierHeader
  78. @brief HTTP header name for iOS bundle ID.
  79. */
  80. static NSString *const kIosBundleIdentifierHeader = @"X-Ios-Bundle-Identifier";
  81. /** @var kFirebaseLocalHeader
  82. @brief HTTP header name for the firebase locale.
  83. */
  84. static NSString *const kFirebaseLocalHeader = @"X-Firebase-Locale";
  85. /** @var kFirebaseAppIDHeader
  86. @brief HTTP header name for the Firebase app ID.
  87. */
  88. static NSString *const kFirebaseAppIDHeader = @"X-Firebase-GMPID";
  89. /** @var kFirebaseHeartbeatHeader
  90. @brief HTTP header name for a Firebase heartbeats payload.
  91. */
  92. static NSString *const kFirebaseHeartbeatHeader = @"X-Firebase-Client";
  93. /** @var kFirebaseAuthCoreFrameworkMarker
  94. @brief The marker in the HTTP header that indicates the request comes from Firebase Auth Core.
  95. */
  96. static NSString *const kFirebaseAuthCoreFrameworkMarker = @"FirebaseCore-iOS";
  97. /** @var kJSONContentType
  98. @brief The value of the HTTP content-type header for JSON payloads.
  99. */
  100. static NSString *const kJSONContentType = @"application/json";
  101. /** @var kErrorDataKey
  102. @brief Key for error data in NSError returned by @c GTMSessionFetcher.
  103. */
  104. static NSString *const kErrorDataKey = @"data";
  105. /** @var kErrorKey
  106. @brief The key for the "error" value in JSON responses from the server.
  107. */
  108. static NSString *const kErrorKey = @"error";
  109. /** @var kErrorsKey
  110. @brief The key for the "errors" value in JSON responses from the server.
  111. */
  112. static NSString *const kErrorsKey = @"errors";
  113. /** @var kReasonKey
  114. @brief The key for the "reason" value in JSON responses from the server.
  115. */
  116. static NSString *const kReasonKey = @"reason";
  117. /** @var kInvalidKeyReasonValue
  118. @brief The value for the "reason" key indicating an invalid API Key was received by the server.
  119. */
  120. static NSString *const kInvalidKeyReasonValue = @"keyInvalid";
  121. /** @var kAppNotAuthorizedReasonValue
  122. @brief The value for the "reason" key indicating the App is not authorized to use Firebase
  123. Authentication.
  124. */
  125. static NSString *const kAppNotAuthorizedReasonValue = @"ipRefererBlocked";
  126. /** @var kErrorMessageKey
  127. @brief The key for an error's "message" value in JSON responses from the server.
  128. */
  129. static NSString *const kErrorMessageKey = @"message";
  130. /** @var kReturnIDPCredentialErrorMessageKey
  131. @brief The key for "errorMessage" value in JSON responses from the server, In case
  132. returnIDPCredential of a verifyAssertion request is set to @YES.
  133. */
  134. static NSString *const kReturnIDPCredentialErrorMessageKey = @"errorMessage";
  135. /** @var kUserNotFoundErrorMessage
  136. @brief This is the error message returned when the user is not found, which means the user
  137. account has been deleted given the token was once valid.
  138. */
  139. static NSString *const kUserNotFoundErrorMessage = @"USER_NOT_FOUND";
  140. /** @var kUserDeletedErrorMessage
  141. @brief This is the error message the server will respond with if the user entered an invalid
  142. email address.
  143. */
  144. static NSString *const kUserDeletedErrorMessage = @"EMAIL_NOT_FOUND";
  145. /** @var kInvalidLocalIDErrorMessage
  146. @brief This is the error message the server responds with if the user local id in the id token
  147. does not exit.
  148. */
  149. static NSString *const kInvalidLocalIDErrorMessage = @"INVALID_LOCAL_ID";
  150. /** @var kUserTokenExpiredErrorMessage
  151. @brief The error returned by the server if the token issue time is older than the account's
  152. valid_since time.
  153. */
  154. static NSString *const kUserTokenExpiredErrorMessage = @"TOKEN_EXPIRED";
  155. /** @var kTooManyRequestsErrorMessage
  156. @brief This is the error message the server will respond with if too many requests were made to
  157. a server method.
  158. */
  159. static NSString *const kTooManyRequestsErrorMessage = @"TOO_MANY_ATTEMPTS_TRY_LATER";
  160. /** @var kInvalidCustomTokenErrorMessage
  161. @brief This is the error message the server will respond with if there is a validation error
  162. with the custom token.
  163. */
  164. static NSString *const kInvalidCustomTokenErrorMessage = @"INVALID_CUSTOM_TOKEN";
  165. /** @var kCustomTokenMismatch
  166. @brief This is the error message the server will respond with if the service account and API key
  167. belong to different projects.
  168. */
  169. static NSString *const kCustomTokenMismatch = @"CREDENTIAL_MISMATCH";
  170. /** @var kInvalidCredentialErrorMessage
  171. @brief This is the error message the server responds with if the IDP token or requestUri is
  172. invalid.
  173. */
  174. static NSString *const kInvalidCredentialErrorMessage = @"INVALID_IDP_RESPONSE";
  175. /** @var kUserDisabledErrorMessage
  176. @brief The error returned by the server if the user account is diabled.
  177. */
  178. static NSString *const kUserDisabledErrorMessage = @"USER_DISABLED";
  179. /** @var kOperationNotAllowedErrorMessage
  180. @brief This is the error message the server will respond with if Admin disables IDP specified by
  181. provider.
  182. */
  183. static NSString *const kOperationNotAllowedErrorMessage = @"OPERATION_NOT_ALLOWED";
  184. /** @var kPasswordLoginDisabledErrorMessage
  185. @brief This is the error message the server responds with if password login is disabled.
  186. */
  187. static NSString *const kPasswordLoginDisabledErrorMessage = @"PASSWORD_LOGIN_DISABLED";
  188. /** @var kEmailAlreadyInUseErrorMessage
  189. @brief This is the error message the server responds with if the email address already exists.
  190. */
  191. static NSString *const kEmailAlreadyInUseErrorMessage = @"EMAIL_EXISTS";
  192. /** @var kInvalidEmailErrorMessage
  193. @brief The error returned by the server if the email is invalid.
  194. */
  195. static NSString *const kInvalidEmailErrorMessage = @"INVALID_EMAIL";
  196. /** @var kInvalidIdentifierErrorMessage
  197. @brief The error returned by the server if the identifier is invalid.
  198. */
  199. static NSString *const kInvalidIdentifierErrorMessage = @"INVALID_IDENTIFIER";
  200. /** @var kWrongPasswordErrorMessage
  201. @brief This is the error message the server will respond with if the user entered a wrong
  202. password.
  203. */
  204. static NSString *const kWrongPasswordErrorMessage = @"INVALID_PASSWORD";
  205. /** @var kCredentialTooOldErrorMessage
  206. @brief This is the error message the server responds with if account change is attempted 5
  207. minutes after signing in.
  208. */
  209. static NSString *const kCredentialTooOldErrorMessage = @"CREDENTIAL_TOO_OLD_LOGIN_AGAIN";
  210. /** @var kFederatedUserIDAlreadyLinkedMessage
  211. @brief This is the error message the server will respond with if the federated user ID has been
  212. already linked with another account.
  213. */
  214. static NSString *const kFederatedUserIDAlreadyLinkedMessage = @"FEDERATED_USER_ID_ALREADY_LINKED";
  215. /** @var kInvalidUserTokenErrorMessage
  216. @brief This is the error message the server responds with if user's saved auth credential is
  217. invalid, and the user needs to sign in again.
  218. */
  219. static NSString *const kInvalidUserTokenErrorMessage = @"INVALID_ID_TOKEN";
  220. /** @var kWeakPasswordErrorMessagePrefix
  221. @brief This is the prefix for the error message the server responds with if user's new password
  222. to be set is too weak.
  223. */
  224. static NSString *const kWeakPasswordErrorMessagePrefix = @"WEAK_PASSWORD";
  225. /** @var kExpiredActionCodeErrorMessage
  226. @brief This is the error message the server will respond with if the action code is expired.
  227. */
  228. static NSString *const kExpiredActionCodeErrorMessage = @"EXPIRED_OOB_CODE";
  229. /** @var kInvalidActionCodeErrorMessage
  230. @brief This is the error message the server will respond with if the action code is invalid.
  231. */
  232. static NSString *const kInvalidActionCodeErrorMessage = @"INVALID_OOB_CODE";
  233. /** @var kMissingEmailErrorMessage
  234. @brief This is the error message the server will respond with if the email address is missing
  235. during a "send password reset email" attempt.
  236. */
  237. static NSString *const kMissingEmailErrorMessage = @"MISSING_EMAIL";
  238. /** @var kInvalidSenderEmailErrorMessage
  239. @brief This is the error message the server will respond with if the sender email is invalid
  240. during a "send password reset email" attempt.
  241. */
  242. static NSString *const kInvalidSenderEmailErrorMessage = @"INVALID_SENDER";
  243. /** @var kInvalidMessagePayloadErrorMessage
  244. @brief This is the error message the server will respond with if there are invalid parameters in
  245. the payload during a "send password reset email" attempt.
  246. */
  247. static NSString *const kInvalidMessagePayloadErrorMessage = @"INVALID_MESSAGE_PAYLOAD";
  248. /** @var kInvalidRecipientEmailErrorMessage
  249. @brief This is the error message the server will respond with if the recipient email is invalid.
  250. */
  251. static NSString *const kInvalidRecipientEmailErrorMessage = @"INVALID_RECIPIENT_EMAIL";
  252. /** @var kMissingIosBundleIDErrorMessage
  253. @brief This is the error message the server will respond with if iOS bundle ID is missing but
  254. the iOS App store ID is provided.
  255. */
  256. static NSString *const kMissingIosBundleIDErrorMessage = @"MISSING_IOS_BUNDLE_ID";
  257. /** @var kMissingAndroidPackageNameErrorMessage
  258. @brief This is the error message the server will respond with if Android Package Name is missing
  259. but the flag indicating the app should be installed is set to true.
  260. */
  261. static NSString *const kMissingAndroidPackageNameErrorMessage = @"MISSING_ANDROID_PACKAGE_NAME";
  262. /** @var kUnauthorizedDomainErrorMessage
  263. @brief This is the error message the server will respond with if the domain of the continue URL
  264. specified is not allowlisted in the Firebase console.
  265. */
  266. static NSString *const kUnauthorizedDomainErrorMessage = @"UNAUTHORIZED_DOMAIN";
  267. /** @var kInvalidProviderIDErrorMessage
  268. @brief This is the error message the server will respond with if the provider id given for the
  269. web operation is invalid.
  270. */
  271. static NSString *const kInvalidProviderIDErrorMessage = @"INVALID_PROVIDER_ID";
  272. /** @var kInvalidDynamicLinkDomainErrorMessage
  273. @brief This is the error message the server will respond with if the dynamic link domain
  274. provided in the request is invalid.
  275. */
  276. static NSString *const kInvalidDynamicLinkDomainErrorMessage = @"INVALID_DYNAMIC_LINK_DOMAIN";
  277. /** @var kInvalidContinueURIErrorMessage
  278. @brief This is the error message the server will respond with if the continue URL provided in
  279. the request is invalid.
  280. */
  281. static NSString *const kInvalidContinueURIErrorMessage = @"INVALID_CONTINUE_URI";
  282. /** @var kMissingContinueURIErrorMessage
  283. @brief This is the error message the server will respond with if there was no continue URI
  284. present in a request that required one.
  285. */
  286. static NSString *const kMissingContinueURIErrorMessage = @"MISSING_CONTINUE_URI";
  287. /** @var kInvalidPhoneNumberErrorMessage
  288. @brief This is the error message the server will respond with if an incorrectly formatted phone
  289. number is provided.
  290. */
  291. static NSString *const kInvalidPhoneNumberErrorMessage = @"INVALID_PHONE_NUMBER";
  292. /** @var kInvalidVerificationCodeErrorMessage
  293. @brief This is the error message the server will respond with if an invalid verification code is
  294. provided.
  295. */
  296. static NSString *const kInvalidVerificationCodeErrorMessage = @"INVALID_CODE";
  297. /** @var kInvalidSessionInfoErrorMessage
  298. @brief This is the error message the server will respond with if an invalid session info
  299. (verification ID) is provided.
  300. */
  301. static NSString *const kInvalidSessionInfoErrorMessage = @"INVALID_SESSION_INFO";
  302. /** @var kSessionExpiredErrorMessage
  303. @brief This is the error message the server will respond with if the SMS code has expired before
  304. it is used.
  305. */
  306. static NSString *const kSessionExpiredErrorMessage = @"SESSION_EXPIRED";
  307. /** @var kMissingOrInvalidNonceErrorMessage
  308. @brief This is the error message the server will respond with if the nonce is missing or
  309. invalid.
  310. */
  311. static NSString *const kMissingOrInvalidNonceErrorMessage = @"MISSING_OR_INVALID_NONCE";
  312. /** @var kMissingAppTokenErrorMessage
  313. @brief This is the error message the server will respond with if the APNS token is missing in a
  314. verifyClient request.
  315. */
  316. static NSString *const kMissingAppTokenErrorMessage = @"MISSING_IOS_APP_TOKEN";
  317. /** @var kMissingAppCredentialErrorMessage
  318. @brief This is the error message the server will respond with if the app token is missing in a
  319. sendVerificationCode request.
  320. */
  321. static NSString *const kMissingAppCredentialErrorMessage = @"MISSING_APP_CREDENTIAL";
  322. /** @var kInvalidAppCredentialErrorMessage
  323. @brief This is the error message the server will respond with if the app credential in a
  324. sendVerificationCode request is invalid.
  325. */
  326. static NSString *const kInvalidAppCredentialErrorMessage = @"INVALID_APP_CREDENTIAL";
  327. /** @var kQuoutaExceededErrorMessage
  328. @brief This is the error message the server will respond with if the quota for SMS text messages
  329. has been exceeded for the project.
  330. */
  331. static NSString *const kQuoutaExceededErrorMessage = @"QUOTA_EXCEEDED";
  332. /** @var kAppNotVerifiedErrorMessage
  333. @brief This is the error message the server will respond with if Firebase could not verify the
  334. app during a phone authentication flow.
  335. */
  336. static NSString *const kAppNotVerifiedErrorMessage = @"APP_NOT_VERIFIED";
  337. /** @var kMissingClientIdentifier
  338. @brief This is the error message the server will respond with if Firebase could not verify the
  339. app during a phone authentication flow when a real phone number is used and app verification
  340. is disabled for testing.
  341. */
  342. static NSString *const kMissingClientIdentifier = @"MISSING_CLIENT_IDENTIFIER";
  343. /** @var kCaptchaCheckFailedErrorMessage
  344. @brief This is the error message the server will respond with if the reCAPTCHA token provided is
  345. invalid.
  346. */
  347. static NSString *const kCaptchaCheckFailedErrorMessage = @"CAPTCHA_CHECK_FAILED";
  348. /** @var kTenantIDMismatch
  349. @brief This is the error message the server will respond with if the tenant id mismatches.
  350. */
  351. static NSString *const kTenantIDMismatch = @"TENANT_ID_MISMATCH";
  352. /** @var kUnsupportedTenantOperation
  353. @brief This is the error message the server will respond with if the operation does not support
  354. multi-tenant.
  355. */
  356. static NSString *const kUnsupportedTenantOperation = @"UNSUPPORTED_TENANT_OPERATION";
  357. /** @var kMissingMFAPendingCredentialErrorMessage
  358. @brief This is the error message the server will respond with if the MFA pending credential is
  359. missing.
  360. */
  361. static NSString *const kMissingMFAPendingCredentialErrorMessage = @"MISSING_MFA_PENDING_CREDENTIAL";
  362. /** @var kMissingMFAEnrollmentIDErrorMessage
  363. @brief This is the error message the server will respond with if the MFA enrollment ID is missing.
  364. */
  365. static NSString *const kMissingMFAEnrollmentIDErrorMessage = @"MISSING_MFA_ENROLLMENT_ID";
  366. /** @var kInvalidMFAPendingCredentialErrorMessage
  367. @brief This is the error message the server will respond with if the MFA pending credential is
  368. invalid.
  369. */
  370. static NSString *const kInvalidMFAPendingCredentialErrorMessage = @"INVALID_MFA_PENDING_CREDENTIAL";
  371. /** @var kMFAEnrollmentNotFoundErrorMessage
  372. @brief This is the error message the server will respond with if the MFA enrollment info is not
  373. found.
  374. */
  375. static NSString *const kMFAEnrollmentNotFoundErrorMessage = @"MFA_ENROLLMENT_NOT_FOUND";
  376. /** @var kAdminOnlyOperationErrorMessage
  377. @brief This is the error message the server will respond with if the operation is admin only.
  378. */
  379. static NSString *const kAdminOnlyOperationErrorMessage = @"ADMIN_ONLY_OPERATION";
  380. /** @var kUnverifiedEmailErrorMessage
  381. @brief This is the error message the server will respond with if the email is unverified.
  382. */
  383. static NSString *const kUnverifiedEmailErrorMessage = @"UNVERIFIED_EMAIL";
  384. /** @var kSecondFactorExistsErrorMessage
  385. @brief This is the error message the server will respond with if the second factor already exsists.
  386. */
  387. static NSString *const kSecondFactorExistsErrorMessage = @"SECOND_FACTOR_EXISTS";
  388. /** @var kSecondFactorLimitExceededErrorMessage
  389. @brief This is the error message the server will respond with if the number of second factor
  390. reaches the limit.
  391. */
  392. static NSString *const kSecondFactorLimitExceededErrorMessage = @"SECOND_FACTOR_LIMIT_EXCEEDED";
  393. /** @var kUnsupportedFirstFactorErrorMessage
  394. @brief This is the error message the server will respond with if the first factor doesn't support
  395. MFA.
  396. */
  397. static NSString *const kUnsupportedFirstFactorErrorMessage = @"UNSUPPORTED_FIRST_FACTOR";
  398. /** @var kBlockingCloudFunctionErrorResponse
  399. @brief This is the error message blocking Cloud Functions.
  400. */
  401. static NSString *const kBlockingCloudFunctionErrorResponse = @"BLOCKING_FUNCTION_ERROR_RESPONSE";
  402. /** @var kEmailChangeNeedsVerificationErrorMessage
  403. @brief This is the error message the server will respond with if changing an unverified email.
  404. */
  405. static NSString *const kEmailChangeNeedsVerificationErrorMessage =
  406. @"EMAIL_CHANGE_NEEDS_VERIFICATION";
  407. /** @var kInvalidPendingToken
  408. @brief Generic IDP error codes.
  409. */
  410. static NSString *const kInvalidPendingToken = @"INVALID_PENDING_TOKEN";
  411. /** @var gBackendImplementation
  412. @brief The singleton FIRAuthBackendImplementation instance to use.
  413. */
  414. static id<FIRAuthBackendImplementation> gBackendImplementation;
  415. /** @class FIRAuthBackendRPCImplementation
  416. @brief The default RPC-based backend implementation.
  417. */
  418. @interface FIRAuthBackendRPCImplementation : NSObject <FIRAuthBackendImplementation>
  419. /** @property RPCIssuer
  420. @brief An instance of FIRAuthBackendRPCIssuer for making RPC requests. Allows the RPC
  421. requests/responses to be easily faked.
  422. */
  423. @property(nonatomic, strong) id<FIRAuthBackendRPCIssuer> RPCIssuer;
  424. @end
  425. @implementation FIRAuthBackend
  426. + (id<FIRAuthBackendImplementation>)implementation {
  427. if (!gBackendImplementation) {
  428. gBackendImplementation = [[FIRAuthBackendRPCImplementation alloc] init];
  429. }
  430. return gBackendImplementation;
  431. }
  432. + (void)setBackendImplementation:(id<FIRAuthBackendImplementation>)backendImplementation {
  433. gBackendImplementation = backendImplementation;
  434. }
  435. + (void)setDefaultBackendImplementationWithRPCIssuer:
  436. (nullable id<FIRAuthBackendRPCIssuer>)RPCIssuer {
  437. FIRAuthBackendRPCImplementation *defaultImplementation =
  438. [[FIRAuthBackendRPCImplementation alloc] init];
  439. if (RPCIssuer) {
  440. defaultImplementation.RPCIssuer = RPCIssuer;
  441. }
  442. gBackendImplementation = defaultImplementation;
  443. }
  444. + (void)createAuthURI:(FIRCreateAuthURIRequest *)request
  445. callback:(FIRCreateAuthURIResponseCallback)callback {
  446. [[self implementation] createAuthURI:request callback:callback];
  447. }
  448. + (void)getAccountInfo:(FIRGetAccountInfoRequest *)request
  449. callback:(FIRGetAccountInfoResponseCallback)callback {
  450. [[self implementation] getAccountInfo:request callback:callback];
  451. }
  452. + (void)getProjectConfig:(FIRGetProjectConfigRequest *)request
  453. callback:(FIRGetProjectConfigResponseCallback)callback {
  454. [[self implementation] getProjectConfig:request callback:callback];
  455. }
  456. + (void)setAccountInfo:(FIRSetAccountInfoRequest *)request
  457. callback:(FIRSetAccountInfoResponseCallback)callback {
  458. [[self implementation] setAccountInfo:request callback:callback];
  459. }
  460. + (void)verifyAssertion:(FIRVerifyAssertionRequest *)request
  461. callback:(FIRVerifyAssertionResponseCallback)callback {
  462. [[self implementation] verifyAssertion:request callback:callback];
  463. }
  464. + (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request
  465. callback:(FIRVerifyCustomTokenResponseCallback)callback {
  466. [[self implementation] verifyCustomToken:request callback:callback];
  467. }
  468. + (void)verifyPassword:(FIRVerifyPasswordRequest *)request
  469. callback:(FIRVerifyPasswordResponseCallback)callback {
  470. [[self implementation] verifyPassword:request callback:callback];
  471. }
  472. + (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
  473. callback:(FIREmailLinkSigninResponseCallback)callback {
  474. [[self implementation] emailLinkSignin:request callback:callback];
  475. }
  476. + (void)secureToken:(FIRSecureTokenRequest *)request
  477. callback:(FIRSecureTokenResponseCallback)callback {
  478. [[self implementation] secureToken:request callback:callback];
  479. }
  480. + (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request
  481. callback:(FIRGetOOBConfirmationCodeResponseCallback)callback {
  482. [[self implementation] getOOBConfirmationCode:request callback:callback];
  483. }
  484. + (void)signUpNewUser:(FIRSignUpNewUserRequest *)request
  485. callback:(FIRSignupNewUserCallback)callback {
  486. [[self implementation] signUpNewUser:request callback:callback];
  487. }
  488. + (void)deleteAccount:(FIRDeleteAccountRequest *)request callback:(FIRDeleteCallBack)callback {
  489. [[self implementation] deleteAccount:request callback:callback];
  490. }
  491. + (void)signInWithGameCenter:(FIRSignInWithGameCenterRequest *)request
  492. callback:(FIRSignInWithGameCenterResponseCallback)callback {
  493. [[self implementation] signInWithGameCenter:request callback:callback];
  494. }
  495. #if TARGET_OS_IOS
  496. + (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request
  497. callback:(FIRSendVerificationCodeResponseCallback)callback {
  498. [[self implementation] sendVerificationCode:request callback:callback];
  499. }
  500. + (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request
  501. callback:(FIRVerifyPhoneNumberResponseCallback)callback {
  502. [[self implementation] verifyPhoneNumber:request callback:callback];
  503. }
  504. + (void)verifyClient:(id)request callback:(FIRVerifyClientResponseCallback)callback {
  505. [[self implementation] verifyClient:request callback:callback];
  506. }
  507. #endif
  508. + (void)revokeToken:(FIRRevokeTokenRequest *)request
  509. callback:(FIRRevokeTokenResponseCallback)callback {
  510. [[self implementation] revokeToken:request callback:callback];
  511. }
  512. + (void)resetPassword:(FIRResetPasswordRequest *)request
  513. callback:(FIRResetPasswordCallback)callback {
  514. [[self implementation] resetPassword:request callback:callback];
  515. }
  516. + (NSString *)authUserAgent {
  517. return [NSString stringWithFormat:@"FirebaseAuth.iOS/%@ %@", FIRFirebaseVersion(),
  518. GTMFetcherStandardUserAgentString(nil)];
  519. }
  520. + (void)requestWithURL:(NSURL *)URL
  521. contentType:(NSString *)contentType
  522. requestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
  523. completionHandler:(void (^)(NSMutableURLRequest *_Nullable))completionHandler {
  524. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
  525. [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
  526. NSString *additionalFrameworkMarker =
  527. requestConfiguration.additionalFrameworkMarker ?: kFirebaseAuthCoreFrameworkMarker;
  528. NSString *clientVersion = [NSString
  529. stringWithFormat:@"iOS/FirebaseSDK/%@/%@", FIRFirebaseVersion(), additionalFrameworkMarker];
  530. [request setValue:clientVersion forHTTPHeaderField:kClientVersionHeader];
  531. NSString *bundleID = [[NSBundle mainBundle] bundleIdentifier];
  532. [request setValue:bundleID forHTTPHeaderField:kIosBundleIdentifierHeader];
  533. NSString *appID = requestConfiguration.appID;
  534. [request setValue:appID forHTTPHeaderField:kFirebaseAppIDHeader];
  535. [request setValue:FIRHeaderValueFromHeartbeatsPayload(
  536. [requestConfiguration.heartbeatLogger flushHeartbeatsIntoPayload])
  537. forHTTPHeaderField:kFirebaseHeartbeatHeader];
  538. NSArray<NSString *> *preferredLocalizations = [NSBundle mainBundle].preferredLocalizations;
  539. if (preferredLocalizations.count) {
  540. NSString *acceptLanguage = preferredLocalizations.firstObject;
  541. [request setValue:acceptLanguage forHTTPHeaderField:@"Accept-Language"];
  542. }
  543. NSString *languageCode = requestConfiguration.languageCode;
  544. if (languageCode.length) {
  545. [request setValue:languageCode forHTTPHeaderField:kFirebaseLocalHeader];
  546. }
  547. if (requestConfiguration.appCheck) {
  548. [requestConfiguration.appCheck
  549. getTokenForcingRefresh:false
  550. completion:^(id<FIRAppCheckTokenResultInterop> _Nonnull tokenResult) {
  551. if (tokenResult.error) {
  552. FIRLogWarning(kFIRLoggerAuth, @"I-AUT000018",
  553. @"Error getting App Check token; using placeholder token "
  554. @"instead. Error: %@",
  555. tokenResult.error);
  556. }
  557. [request setValue:tokenResult.token
  558. forHTTPHeaderField:@"X-Firebase-AppCheck"];
  559. completionHandler(request);
  560. }];
  561. } else {
  562. completionHandler(request);
  563. }
  564. }
  565. @end
  566. @interface FIRAuthBackendRPCIssuerImplementation : NSObject <FIRAuthBackendRPCIssuer>
  567. @end
  568. @implementation FIRAuthBackendRPCIssuerImplementation {
  569. /** @var The session fetcher service.
  570. */
  571. GTMSessionFetcherService *_fetcherService;
  572. }
  573. - (instancetype)init {
  574. self = [super init];
  575. if (self) {
  576. _fetcherService = [[GTMSessionFetcherService alloc] init];
  577. _fetcherService.userAgent = [FIRAuthBackend authUserAgent];
  578. _fetcherService.callbackQueue = FIRAuthGlobalWorkQueue();
  579. // Avoid reusing the session to prevent
  580. // https://github.com/firebase/firebase-ios-sdk/issues/1261
  581. _fetcherService.reuseSession = NO;
  582. }
  583. return self;
  584. }
  585. - (void)asyncPostToURLWithRequestConfiguration:(FIRAuthRequestConfiguration *)requestConfiguration
  586. URL:(NSURL *)URL
  587. body:(nullable NSData *)body
  588. contentType:(NSString *)contentType
  589. completionHandler:
  590. (void (^)(NSData *_Nullable, NSError *_Nullable))handler {
  591. [FIRAuthBackend requestWithURL:URL
  592. contentType:contentType
  593. requestConfiguration:requestConfiguration
  594. completionHandler:^(NSMutableURLRequest *request) {
  595. GTMSessionFetcher *fetcher = [self->_fetcherService fetcherWithRequest:request];
  596. NSString *emulatorHostAndPort = requestConfiguration.emulatorHostAndPort;
  597. if (emulatorHostAndPort) {
  598. fetcher.allowLocalhostRequest = YES;
  599. fetcher.allowedInsecureSchemes = @[ @"http" ];
  600. }
  601. fetcher.bodyData = body;
  602. [fetcher beginFetchWithCompletionHandler:handler];
  603. }];
  604. }
  605. @end
  606. @implementation FIRAuthBackendRPCImplementation
  607. - (instancetype)init {
  608. self = [super init];
  609. if (self) {
  610. _RPCIssuer = [[FIRAuthBackendRPCIssuerImplementation alloc] init];
  611. }
  612. return self;
  613. }
  614. - (void)createAuthURI:(FIRCreateAuthURIRequest *)request
  615. callback:(FIRCreateAuthURIResponseCallback)callback {
  616. FIRCreateAuthURIResponse *response = [[FIRCreateAuthURIResponse alloc] init];
  617. [self postWithRequest:request
  618. response:response
  619. callback:^(NSError *error) {
  620. if (error) {
  621. callback(nil, error);
  622. } else {
  623. callback(response, nil);
  624. }
  625. }];
  626. }
  627. - (void)getAccountInfo:(FIRGetAccountInfoRequest *)request
  628. callback:(FIRGetAccountInfoResponseCallback)callback {
  629. FIRGetAccountInfoResponse *response = [[FIRGetAccountInfoResponse alloc] init];
  630. [self postWithRequest:request
  631. response:response
  632. callback:^(NSError *error) {
  633. if (error) {
  634. callback(nil, error);
  635. } else {
  636. callback(response, nil);
  637. }
  638. }];
  639. }
  640. - (void)getProjectConfig:(FIRGetProjectConfigRequest *)request
  641. callback:(FIRGetProjectConfigResponseCallback)callback {
  642. FIRGetProjectConfigResponse *response = [[FIRGetProjectConfigResponse alloc] init];
  643. [self postWithRequest:request
  644. response:response
  645. callback:^(NSError *error) {
  646. if (error) {
  647. callback(nil, error);
  648. } else {
  649. callback(response, nil);
  650. }
  651. }];
  652. }
  653. - (void)setAccountInfo:(FIRSetAccountInfoRequest *)request
  654. callback:(FIRSetAccountInfoResponseCallback)callback {
  655. FIRSetAccountInfoResponse *response = [[FIRSetAccountInfoResponse alloc] init];
  656. [self postWithRequest:request
  657. response:response
  658. callback:^(NSError *error) {
  659. if (error) {
  660. callback(nil, error);
  661. } else {
  662. callback(response, nil);
  663. }
  664. }];
  665. }
  666. - (void)verifyAssertion:(FIRVerifyAssertionRequest *)request
  667. callback:(FIRVerifyAssertionResponseCallback)callback {
  668. FIRVerifyAssertionResponse *response = [[FIRVerifyAssertionResponse alloc] init];
  669. [self
  670. postWithRequest:request
  671. response:response
  672. callback:^(NSError *error) {
  673. if (error) {
  674. callback(nil, error);
  675. } else {
  676. if (!response.IDToken && response.MFAInfo) {
  677. #if TARGET_OS_IOS
  678. NSMutableArray<FIRMultiFactorInfo *> *multiFactorInfo = [NSMutableArray array];
  679. for (FIRAuthProtoMFAEnrollment *MFAEnrollment in response.MFAInfo) {
  680. FIRPhoneMultiFactorInfo *info =
  681. [[FIRPhoneMultiFactorInfo alloc] initWithProto:MFAEnrollment];
  682. [multiFactorInfo addObject:info];
  683. }
  684. NSError *multiFactorRequiredError = [FIRAuthErrorUtils
  685. secondFactorRequiredErrorWithPendingCredential:response.MFAPendingCredential
  686. hints:multiFactorInfo
  687. auth:request.requestConfiguration
  688. .auth];
  689. callback(nil, multiFactorRequiredError);
  690. #endif
  691. } else {
  692. callback(response, nil);
  693. }
  694. }
  695. }];
  696. }
  697. - (void)verifyCustomToken:(FIRVerifyCustomTokenRequest *)request
  698. callback:(FIRVerifyCustomTokenResponseCallback)callback {
  699. FIRVerifyCustomTokenResponse *response = [[FIRVerifyCustomTokenResponse alloc] init];
  700. [self postWithRequest:request
  701. response:response
  702. callback:^(NSError *error) {
  703. if (error) {
  704. callback(nil, error);
  705. } else {
  706. callback(response, nil);
  707. }
  708. }];
  709. }
  710. - (void)verifyPassword:(FIRVerifyPasswordRequest *)request
  711. callback:(FIRVerifyPasswordResponseCallback)callback {
  712. FIRVerifyPasswordResponse *response = [[FIRVerifyPasswordResponse alloc] init];
  713. [self
  714. postWithRequest:request
  715. response:response
  716. callback:^(NSError *error) {
  717. if (error) {
  718. callback(nil, error);
  719. } else {
  720. if (!response.IDToken && response.MFAInfo) {
  721. #if TARGET_OS_IOS
  722. NSMutableArray<FIRMultiFactorInfo *> *multiFactorInfo = [NSMutableArray array];
  723. for (FIRAuthProtoMFAEnrollment *MFAEnrollment in response.MFAInfo) {
  724. FIRPhoneMultiFactorInfo *info =
  725. [[FIRPhoneMultiFactorInfo alloc] initWithProto:MFAEnrollment];
  726. [multiFactorInfo addObject:info];
  727. }
  728. NSError *multiFactorRequiredError = [FIRAuthErrorUtils
  729. secondFactorRequiredErrorWithPendingCredential:response.MFAPendingCredential
  730. hints:multiFactorInfo
  731. auth:request.requestConfiguration
  732. .auth];
  733. callback(nil, multiFactorRequiredError);
  734. #endif
  735. } else {
  736. callback(response, nil);
  737. }
  738. }
  739. }];
  740. }
  741. - (void)emailLinkSignin:(FIREmailLinkSignInRequest *)request
  742. callback:(FIREmailLinkSigninResponseCallback)callback {
  743. FIREmailLinkSignInResponse *response = [[FIREmailLinkSignInResponse alloc] init];
  744. [self
  745. postWithRequest:request
  746. response:response
  747. callback:^(NSError *error) {
  748. if (error) {
  749. callback(nil, error);
  750. } else {
  751. if (!response.IDToken && response.MFAInfo) {
  752. #if TARGET_OS_IOS
  753. NSMutableArray<FIRMultiFactorInfo *> *multiFactorInfo = [NSMutableArray array];
  754. for (FIRAuthProtoMFAEnrollment *MFAEnrollment in response.MFAInfo) {
  755. FIRPhoneMultiFactorInfo *info =
  756. [[FIRPhoneMultiFactorInfo alloc] initWithProto:MFAEnrollment];
  757. [multiFactorInfo addObject:info];
  758. }
  759. NSError *multiFactorRequiredError = [FIRAuthErrorUtils
  760. secondFactorRequiredErrorWithPendingCredential:response.MFAPendingCredential
  761. hints:multiFactorInfo
  762. auth:request.requestConfiguration
  763. .auth];
  764. callback(nil, multiFactorRequiredError);
  765. #endif
  766. } else {
  767. callback(response, nil);
  768. }
  769. }
  770. }];
  771. }
  772. - (void)secureToken:(FIRSecureTokenRequest *)request
  773. callback:(FIRSecureTokenResponseCallback)callback {
  774. FIRSecureTokenResponse *response = [[FIRSecureTokenResponse alloc] init];
  775. [self postWithRequest:request
  776. response:response
  777. callback:^(NSError *error) {
  778. if (error) {
  779. callback(nil, error);
  780. } else {
  781. callback(response, nil);
  782. }
  783. }];
  784. }
  785. - (void)getOOBConfirmationCode:(FIRGetOOBConfirmationCodeRequest *)request
  786. callback:(FIRGetOOBConfirmationCodeResponseCallback)callback {
  787. FIRGetOOBConfirmationCodeResponse *response = [[FIRGetOOBConfirmationCodeResponse alloc] init];
  788. [self postWithRequest:request
  789. response:response
  790. callback:^(NSError *error) {
  791. if (error) {
  792. callback(nil, error);
  793. } else {
  794. callback(response, nil);
  795. }
  796. }];
  797. }
  798. - (void)signUpNewUser:(FIRSignUpNewUserRequest *)request
  799. callback:(FIRSignupNewUserCallback)callback {
  800. FIRSignUpNewUserResponse *response = [[FIRSignUpNewUserResponse alloc] init];
  801. [self postWithRequest:request
  802. response:response
  803. callback:^(NSError *error) {
  804. if (error) {
  805. callback(nil, error);
  806. } else {
  807. callback(response, nil);
  808. }
  809. }];
  810. }
  811. - (void)deleteAccount:(FIRDeleteAccountRequest *)request callback:(FIRDeleteCallBack)callback {
  812. FIRDeleteAccountResponse *response = [[FIRDeleteAccountResponse alloc] init];
  813. [self postWithRequest:request response:response callback:callback];
  814. }
  815. #if TARGET_OS_IOS
  816. - (void)sendVerificationCode:(FIRSendVerificationCodeRequest *)request
  817. callback:(FIRSendVerificationCodeResponseCallback)callback {
  818. FIRSendVerificationCodeResponse *response = [[FIRSendVerificationCodeResponse alloc] init];
  819. [self postWithRequest:request
  820. response:response
  821. callback:^(NSError *error) {
  822. if (error) {
  823. callback(nil, error);
  824. } else {
  825. callback(response, error);
  826. }
  827. }];
  828. }
  829. - (void)verifyPhoneNumber:(FIRVerifyPhoneNumberRequest *)request
  830. callback:(FIRVerifyPhoneNumberResponseCallback)callback {
  831. FIRVerifyPhoneNumberResponse *response = [[FIRVerifyPhoneNumberResponse alloc] init];
  832. [self
  833. postWithRequest:request
  834. response:response
  835. callback:^(NSError *error) {
  836. if (error) {
  837. callback(nil, error);
  838. return;
  839. }
  840. // Check whether or not the successful response is actually the special case phone
  841. // auth flow that returns a temporary proof and phone number.
  842. if (response.phoneNumber.length && response.temporaryProof.length) {
  843. FIRPhoneAuthCredential *credential =
  844. [[FIRPhoneAuthCredential alloc] initWithTemporaryProof:response.temporaryProof
  845. phoneNumber:response.phoneNumber
  846. providerID:FIRPhoneAuthProviderID];
  847. callback(nil, [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:nil
  848. credential:credential
  849. email:nil]);
  850. return;
  851. }
  852. callback(response, nil);
  853. }];
  854. }
  855. - (void)verifyClient:(id)request callback:(FIRVerifyClientResponseCallback)callback {
  856. FIRVerifyClientResponse *response = [[FIRVerifyClientResponse alloc] init];
  857. [self postWithRequest:request
  858. response:response
  859. callback:^(NSError *error) {
  860. if (error) {
  861. callback(nil, error);
  862. return;
  863. }
  864. callback(response, nil);
  865. }];
  866. }
  867. #endif
  868. - (void)revokeToken:(FIRRevokeTokenRequest *)request
  869. callback:(FIRRevokeTokenResponseCallback)callback {
  870. FIRRevokeTokenResponse *response = [[FIRRevokeTokenResponse alloc] init];
  871. [self
  872. postWithRequest:request
  873. response:response
  874. callback:^(NSError *error) {
  875. if (error) {
  876. callback(nil, [FIRAuthErrorUtils
  877. invalidCredentialErrorWithMessage:[error localizedDescription]]);
  878. return;
  879. }
  880. callback(response, nil);
  881. }];
  882. }
  883. - (void)resetPassword:(FIRResetPasswordRequest *)request
  884. callback:(FIRResetPasswordCallback)callback {
  885. FIRResetPasswordResponse *response = [[FIRResetPasswordResponse alloc] init];
  886. [self postWithRequest:request
  887. response:response
  888. callback:^(NSError *error) {
  889. if (error) {
  890. callback(nil, error);
  891. return;
  892. }
  893. callback(response, nil);
  894. }];
  895. }
  896. - (void)signInWithGameCenter:(FIRSignInWithGameCenterRequest *)request
  897. callback:(FIRSignInWithGameCenterResponseCallback)callback {
  898. FIRSignInWithGameCenterResponse *response = [[FIRSignInWithGameCenterResponse alloc] init];
  899. [self postWithRequest:request
  900. response:response
  901. callback:^(NSError *error) {
  902. if (error) {
  903. if (callback) {
  904. callback(nil, error);
  905. }
  906. } else {
  907. if (callback) {
  908. callback(response, nil);
  909. }
  910. }
  911. }];
  912. }
  913. #pragma mark - Generic RPC handling methods
  914. /** @fn postWithRequest:response:callback:
  915. @brief Calls the RPC using HTTP POST.
  916. @remarks Possible error responses:
  917. @see FIRAuthInternalErrorCodeRPCRequestEncodingError
  918. @see FIRAuthInternalErrorCodeJSONSerializationError
  919. @see FIRAuthInternalErrorCodeNetworkError
  920. @see FIRAuthInternalErrorCodeUnexpectedErrorResponse
  921. @see FIRAuthInternalErrorCodeUnexpectedResponse
  922. @see FIRAuthInternalErrorCodeRPCResponseDecodingError
  923. @param request The request.
  924. @param response The empty response to be filled.
  925. @param callback The callback for both success and failure.
  926. */
  927. - (void)postWithRequest:(id<FIRAuthRPCRequest>)request
  928. response:(id<FIRAuthRPCResponse>)response
  929. callback:(void (^)(NSError *_Nullable error))callback {
  930. NSError *error;
  931. NSData *bodyData;
  932. if ([request containsPostBody]) {
  933. id postBody = [request unencodedHTTPRequestBodyWithError:&error];
  934. if (!postBody) {
  935. callback([FIRAuthErrorUtils RPCRequestEncodingErrorWithUnderlyingError:error]);
  936. return;
  937. }
  938. NSJSONWritingOptions JSONWritingOptions = 0;
  939. #if DEBUG
  940. JSONWritingOptions |= NSJSONWritingPrettyPrinted;
  941. #endif
  942. if ([NSJSONSerialization isValidJSONObject:postBody]) {
  943. bodyData = [NSJSONSerialization dataWithJSONObject:postBody
  944. options:JSONWritingOptions
  945. error:&error];
  946. if (!bodyData) {
  947. // This is an untested case. This happens exclusively when there is an error in the
  948. // framework implementation of dataWithJSONObject:options:error:. This shouldn't normally
  949. // occur as isValidJSONObject: should return NO in any case we should encounter an error.
  950. error = [FIRAuthErrorUtils JSONSerializationErrorWithUnderlyingError:error];
  951. }
  952. } else {
  953. error = [FIRAuthErrorUtils JSONSerializationErrorForUnencodableType];
  954. }
  955. if (!bodyData) {
  956. callback(error);
  957. return;
  958. }
  959. }
  960. [_RPCIssuer
  961. asyncPostToURLWithRequestConfiguration:[request requestConfiguration]
  962. URL:[request requestURL]
  963. body:bodyData
  964. contentType:kJSONContentType
  965. completionHandler:^(NSData *data, NSError *error) {
  966. // If there is an error with no body data at all, then this must be a
  967. // network error.
  968. if (error && !data) {
  969. callback([FIRAuthErrorUtils networkErrorWithUnderlyingError:error]);
  970. return;
  971. }
  972. // Try to decode the HTTP response data which may contain either a
  973. // successful response or error message.
  974. NSError *jsonError;
  975. NSDictionary *dictionary =
  976. [NSJSONSerialization JSONObjectWithData:data
  977. options:NSJSONReadingMutableLeaves
  978. error:&jsonError];
  979. if (!dictionary) {
  980. if (error) {
  981. // We have an error, but we couldn't decode the body, so we have no
  982. // additional information other than the raw response and the
  983. // original NSError (the jsonError is infered by the error code
  984. // (FIRAuthErrorCodeUnexpectedHTTPResponse, and is irrelevant.)
  985. callback([FIRAuthErrorUtils
  986. unexpectedErrorResponseWithData:data
  987. underlyingError:error]);
  988. } else {
  989. // This is supposed to be a "successful" response, but we couldn't
  990. // deserialize the body.
  991. callback([FIRAuthErrorUtils unexpectedResponseWithData:data
  992. underlyingError:jsonError]);
  993. }
  994. return;
  995. }
  996. if (![dictionary isKindOfClass:[NSDictionary class]]) {
  997. if (error) {
  998. callback([FIRAuthErrorUtils
  999. unexpectedErrorResponseWithDeserializedResponse:dictionary
  1000. underlyingError:error]);
  1001. } else {
  1002. callback([FIRAuthErrorUtils
  1003. unexpectedResponseWithDeserializedResponse:dictionary]);
  1004. }
  1005. return;
  1006. }
  1007. // At this point we either have an error with successfully decoded
  1008. // details in the body, or we have a response which must pass further
  1009. // validation before we know it's truly successful. We deal with the
  1010. // case where we have an error with successfully decoded error details
  1011. // first:
  1012. if (error) {
  1013. NSDictionary *errorDictionary = dictionary[kErrorKey];
  1014. if ([errorDictionary isKindOfClass:[NSDictionary class]]) {
  1015. id<NSObject> errorMessage = errorDictionary[kErrorMessageKey];
  1016. if ([errorMessage isKindOfClass:[NSString class]]) {
  1017. NSString *errorMessageString = (NSString *)errorMessage;
  1018. // Contruct client error.
  1019. NSError *clientError = [[self class]
  1020. clientErrorWithServerErrorMessage:errorMessageString
  1021. errorDictionary:errorDictionary
  1022. response:response];
  1023. if (clientError) {
  1024. callback(clientError);
  1025. return;
  1026. }
  1027. }
  1028. // Not a message we know, return the message directly.
  1029. if (errorMessage) {
  1030. NSError *unexpecterErrorResponse = [FIRAuthErrorUtils
  1031. unexpectedErrorResponseWithDeserializedResponse:
  1032. errorDictionary
  1033. underlyingError:error];
  1034. callback(unexpecterErrorResponse);
  1035. return;
  1036. }
  1037. }
  1038. // No error message at all, return the decoded response.
  1039. callback([FIRAuthErrorUtils
  1040. unexpectedErrorResponseWithDeserializedResponse:dictionary
  1041. underlyingError:error]);
  1042. return;
  1043. }
  1044. // Finally, we try to populate the response object with the JSON
  1045. // values.
  1046. if (![response setWithDictionary:dictionary error:&error]) {
  1047. callback([FIRAuthErrorUtils
  1048. RPCResponseDecodingErrorWithDeserializedResponse:dictionary
  1049. underlyingError:error]);
  1050. return;
  1051. }
  1052. // In case returnIDPCredential of a verifyAssertion request is set to
  1053. // @YES, the server may return a 200 with a response that may contain a
  1054. // server error.
  1055. if ([request isKindOfClass:[FIRVerifyAssertionRequest class]]) {
  1056. FIRVerifyAssertionRequest *verifyAssertionRequest =
  1057. (FIRVerifyAssertionRequest *)request;
  1058. if (verifyAssertionRequest.returnIDPCredential) {
  1059. NSString *errorMessage =
  1060. dictionary[kReturnIDPCredentialErrorMessageKey];
  1061. if ([errorMessage isKindOfClass:[NSString class]]) {
  1062. NSString *errorString = (NSString *)errorMessage;
  1063. NSError *clientError =
  1064. [[self class] clientErrorWithServerErrorMessage:errorString
  1065. errorDictionary:@{}
  1066. response:response];
  1067. if (clientError) {
  1068. callback(clientError);
  1069. return;
  1070. }
  1071. }
  1072. }
  1073. }
  1074. // Success! The response object originally passed in can be used by the
  1075. // caller.
  1076. callback(nil);
  1077. }];
  1078. }
  1079. /** @fn clientErrorWithServerErrorMessage:errorDictionary:
  1080. @brief Translates known server errors to client errors.
  1081. @param serverErrorMessage The error message from the server.
  1082. @param errorDictionary The error part of the response from the server.
  1083. @param response The response from the server RPC.
  1084. @return A client error, if any.
  1085. */
  1086. + (nullable NSError *)clientErrorWithServerErrorMessage:(NSString *)serverErrorMessage
  1087. errorDictionary:(NSDictionary *)errorDictionary
  1088. response:(id<FIRAuthRPCResponse>)response {
  1089. NSString *shortErrorMessage = serverErrorMessage;
  1090. NSString *serverDetailErrorMessage;
  1091. NSRange colonRange = [serverErrorMessage rangeOfString:@":"];
  1092. if (colonRange.location != NSNotFound) {
  1093. shortErrorMessage = [serverErrorMessage substringToIndex:colonRange.location];
  1094. shortErrorMessage =
  1095. [shortErrorMessage stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  1096. serverDetailErrorMessage = [serverErrorMessage substringFromIndex:colonRange.location + 1];
  1097. serverDetailErrorMessage = [serverDetailErrorMessage
  1098. stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  1099. }
  1100. // Delegate the responsibility for constructing the client error to the response object,
  1101. // if possible.
  1102. SEL clientErrorWithServerErrorMessageSelector = @selector(clientErrorWithShortErrorMessage:
  1103. detailErrorMessage:);
  1104. if ([response respondsToSelector:clientErrorWithServerErrorMessageSelector]) {
  1105. NSError *error = [response clientErrorWithShortErrorMessage:shortErrorMessage
  1106. detailErrorMessage:serverDetailErrorMessage];
  1107. if (error) {
  1108. return error;
  1109. }
  1110. }
  1111. if ([shortErrorMessage isEqualToString:kUserNotFoundErrorMessage]) {
  1112. return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage];
  1113. }
  1114. if ([shortErrorMessage isEqualToString:kUserDeletedErrorMessage]) {
  1115. return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage];
  1116. }
  1117. if ([shortErrorMessage isEqualToString:kInvalidLocalIDErrorMessage]) {
  1118. // This case shouldn't be necessary but it is for now: b/27908364 .
  1119. return [FIRAuthErrorUtils userNotFoundErrorWithMessage:serverDetailErrorMessage];
  1120. }
  1121. if ([shortErrorMessage isEqualToString:kUserTokenExpiredErrorMessage]) {
  1122. return [FIRAuthErrorUtils userTokenExpiredErrorWithMessage:serverDetailErrorMessage];
  1123. }
  1124. if ([shortErrorMessage isEqualToString:kTooManyRequestsErrorMessage]) {
  1125. return [FIRAuthErrorUtils tooManyRequestsErrorWithMessage:serverDetailErrorMessage];
  1126. }
  1127. if ([shortErrorMessage isEqualToString:kInvalidCustomTokenErrorMessage]) {
  1128. return [FIRAuthErrorUtils invalidCustomTokenErrorWithMessage:serverDetailErrorMessage];
  1129. }
  1130. if ([shortErrorMessage isEqualToString:kCustomTokenMismatch]) {
  1131. return [FIRAuthErrorUtils customTokenMistmatchErrorWithMessage:serverDetailErrorMessage];
  1132. }
  1133. if ([shortErrorMessage isEqualToString:kInvalidCredentialErrorMessage] ||
  1134. [shortErrorMessage isEqualToString:kInvalidPendingToken]) {
  1135. return [FIRAuthErrorUtils invalidCredentialErrorWithMessage:serverDetailErrorMessage];
  1136. }
  1137. if ([shortErrorMessage isEqualToString:kUserDisabledErrorMessage]) {
  1138. return [FIRAuthErrorUtils userDisabledErrorWithMessage:serverDetailErrorMessage];
  1139. }
  1140. if ([shortErrorMessage isEqualToString:kOperationNotAllowedErrorMessage]) {
  1141. return [FIRAuthErrorUtils operationNotAllowedErrorWithMessage:serverDetailErrorMessage];
  1142. }
  1143. if ([shortErrorMessage isEqualToString:kPasswordLoginDisabledErrorMessage]) {
  1144. return [FIRAuthErrorUtils operationNotAllowedErrorWithMessage:serverDetailErrorMessage];
  1145. }
  1146. if ([shortErrorMessage isEqualToString:kEmailAlreadyInUseErrorMessage]) {
  1147. return [FIRAuthErrorUtils emailAlreadyInUseErrorWithEmail:nil];
  1148. }
  1149. if ([shortErrorMessage isEqualToString:kInvalidEmailErrorMessage]) {
  1150. return [FIRAuthErrorUtils invalidEmailErrorWithMessage:serverDetailErrorMessage];
  1151. }
  1152. // "INVALID_IDENTIFIER" can be returned by createAuthURI RPC. Considering email addresses are
  1153. // currently the only identifiers, we surface the FIRAuthErrorCodeInvalidEmail error code in this
  1154. // case.
  1155. if ([shortErrorMessage isEqualToString:kInvalidIdentifierErrorMessage]) {
  1156. return [FIRAuthErrorUtils invalidEmailErrorWithMessage:serverDetailErrorMessage];
  1157. }
  1158. if ([shortErrorMessage isEqualToString:kWrongPasswordErrorMessage]) {
  1159. return [FIRAuthErrorUtils wrongPasswordErrorWithMessage:serverDetailErrorMessage];
  1160. }
  1161. if ([shortErrorMessage isEqualToString:kCredentialTooOldErrorMessage]) {
  1162. return [FIRAuthErrorUtils requiresRecentLoginErrorWithMessage:serverDetailErrorMessage];
  1163. }
  1164. if ([shortErrorMessage isEqualToString:kInvalidUserTokenErrorMessage]) {
  1165. return [FIRAuthErrorUtils invalidUserTokenErrorWithMessage:serverDetailErrorMessage];
  1166. }
  1167. if ([shortErrorMessage isEqualToString:kFederatedUserIDAlreadyLinkedMessage]) {
  1168. FIROAuthCredential *credential;
  1169. NSString *email;
  1170. if ([response isKindOfClass:[FIRVerifyAssertionResponse class]]) {
  1171. FIRVerifyAssertionResponse *verifyAssertion = (FIRVerifyAssertionResponse *)response;
  1172. credential = [[FIROAuthCredential alloc] initWithVerifyAssertionResponse:verifyAssertion];
  1173. email = verifyAssertion.email;
  1174. }
  1175. return [FIRAuthErrorUtils credentialAlreadyInUseErrorWithMessage:serverDetailErrorMessage
  1176. credential:credential
  1177. email:email];
  1178. }
  1179. if ([shortErrorMessage isEqualToString:kWeakPasswordErrorMessagePrefix]) {
  1180. return [FIRAuthErrorUtils weakPasswordErrorWithServerResponseReason:serverDetailErrorMessage];
  1181. }
  1182. if ([shortErrorMessage isEqualToString:kExpiredActionCodeErrorMessage]) {
  1183. return [FIRAuthErrorUtils expiredActionCodeErrorWithMessage:serverDetailErrorMessage];
  1184. }
  1185. if ([shortErrorMessage isEqualToString:kInvalidActionCodeErrorMessage]) {
  1186. return [FIRAuthErrorUtils invalidActionCodeErrorWithMessage:serverDetailErrorMessage];
  1187. }
  1188. if ([shortErrorMessage isEqualToString:kMissingEmailErrorMessage]) {
  1189. return [FIRAuthErrorUtils missingEmailErrorWithMessage:serverDetailErrorMessage];
  1190. }
  1191. if ([shortErrorMessage isEqualToString:kInvalidSenderEmailErrorMessage]) {
  1192. return [FIRAuthErrorUtils invalidSenderErrorWithMessage:serverDetailErrorMessage];
  1193. }
  1194. if ([shortErrorMessage isEqualToString:kInvalidMessagePayloadErrorMessage]) {
  1195. return [FIRAuthErrorUtils invalidMessagePayloadErrorWithMessage:serverDetailErrorMessage];
  1196. }
  1197. if ([shortErrorMessage isEqualToString:kInvalidRecipientEmailErrorMessage]) {
  1198. return [FIRAuthErrorUtils invalidRecipientEmailErrorWithMessage:serverDetailErrorMessage];
  1199. }
  1200. if ([shortErrorMessage isEqualToString:kMissingIosBundleIDErrorMessage]) {
  1201. return [FIRAuthErrorUtils missingIosBundleIDErrorWithMessage:serverDetailErrorMessage];
  1202. }
  1203. if ([shortErrorMessage isEqualToString:kMissingAndroidPackageNameErrorMessage]) {
  1204. return [FIRAuthErrorUtils missingAndroidPackageNameErrorWithMessage:serverDetailErrorMessage];
  1205. }
  1206. if ([shortErrorMessage isEqualToString:kUnauthorizedDomainErrorMessage]) {
  1207. return [FIRAuthErrorUtils unauthorizedDomainErrorWithMessage:serverDetailErrorMessage];
  1208. }
  1209. if ([shortErrorMessage isEqualToString:kInvalidContinueURIErrorMessage]) {
  1210. return [FIRAuthErrorUtils invalidContinueURIErrorWithMessage:serverDetailErrorMessage];
  1211. }
  1212. if ([shortErrorMessage isEqualToString:kInvalidProviderIDErrorMessage]) {
  1213. return [FIRAuthErrorUtils invalidProviderIDErrorWithMessage:serverDetailErrorMessage];
  1214. }
  1215. if ([shortErrorMessage isEqualToString:kInvalidDynamicLinkDomainErrorMessage]) {
  1216. return [FIRAuthErrorUtils invalidDynamicLinkDomainErrorWithMessage:serverDetailErrorMessage];
  1217. }
  1218. if ([shortErrorMessage isEqualToString:kMissingContinueURIErrorMessage]) {
  1219. return [FIRAuthErrorUtils missingContinueURIErrorWithMessage:serverDetailErrorMessage];
  1220. }
  1221. if ([shortErrorMessage isEqualToString:kInvalidPhoneNumberErrorMessage]) {
  1222. return [FIRAuthErrorUtils invalidPhoneNumberErrorWithMessage:serverDetailErrorMessage];
  1223. }
  1224. if ([shortErrorMessage isEqualToString:kInvalidSessionInfoErrorMessage]) {
  1225. return [FIRAuthErrorUtils invalidVerificationIDErrorWithMessage:serverDetailErrorMessage];
  1226. }
  1227. if ([shortErrorMessage isEqualToString:kInvalidVerificationCodeErrorMessage]) {
  1228. return [FIRAuthErrorUtils invalidVerificationCodeErrorWithMessage:serverDetailErrorMessage];
  1229. }
  1230. if ([shortErrorMessage isEqualToString:kSessionExpiredErrorMessage]) {
  1231. return [FIRAuthErrorUtils sessionExpiredErrorWithMessage:serverDetailErrorMessage];
  1232. }
  1233. if ([shortErrorMessage isEqualToString:kMissingAppTokenErrorMessage]) {
  1234. return [FIRAuthErrorUtils missingAppTokenErrorWithUnderlyingError:nil];
  1235. }
  1236. if ([shortErrorMessage isEqualToString:kMissingAppCredentialErrorMessage]) {
  1237. return [FIRAuthErrorUtils missingAppCredentialWithMessage:serverDetailErrorMessage];
  1238. }
  1239. if ([shortErrorMessage isEqualToString:kInvalidAppCredentialErrorMessage]) {
  1240. return [FIRAuthErrorUtils invalidAppCredentialWithMessage:serverDetailErrorMessage];
  1241. }
  1242. if ([shortErrorMessage isEqualToString:kQuoutaExceededErrorMessage]) {
  1243. return [FIRAuthErrorUtils quotaExceededErrorWithMessage:serverErrorMessage];
  1244. }
  1245. if ([shortErrorMessage isEqualToString:kAppNotVerifiedErrorMessage]) {
  1246. return [FIRAuthErrorUtils appNotVerifiedErrorWithMessage:serverErrorMessage];
  1247. }
  1248. if ([shortErrorMessage isEqualToString:kMissingClientIdentifier]) {
  1249. return [FIRAuthErrorUtils missingClientIdentifierErrorWithMessage:serverErrorMessage];
  1250. }
  1251. if ([shortErrorMessage isEqualToString:kCaptchaCheckFailedErrorMessage]) {
  1252. return [FIRAuthErrorUtils captchaCheckFailedErrorWithMessage:serverErrorMessage];
  1253. }
  1254. if ([shortErrorMessage isEqualToString:kMissingOrInvalidNonceErrorMessage]) {
  1255. return [FIRAuthErrorUtils missingOrInvalidNonceErrorWithMessage:serverDetailErrorMessage];
  1256. }
  1257. if ([shortErrorMessage isEqualToString:kMissingMFAPendingCredentialErrorMessage]) {
  1258. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeMissingMultiFactorSession
  1259. message:serverErrorMessage];
  1260. }
  1261. if ([shortErrorMessage isEqualToString:kMissingMFAEnrollmentIDErrorMessage]) {
  1262. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeMissingMultiFactorInfo
  1263. message:serverErrorMessage];
  1264. }
  1265. if ([shortErrorMessage isEqualToString:kInvalidMFAPendingCredentialErrorMessage]) {
  1266. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeInvalidMultiFactorSession
  1267. message:serverErrorMessage];
  1268. }
  1269. if ([shortErrorMessage isEqualToString:kMFAEnrollmentNotFoundErrorMessage]) {
  1270. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeMultiFactorInfoNotFound
  1271. message:serverErrorMessage];
  1272. }
  1273. if ([shortErrorMessage isEqualToString:kAdminOnlyOperationErrorMessage]) {
  1274. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeAdminRestrictedOperation
  1275. message:serverErrorMessage];
  1276. }
  1277. if ([shortErrorMessage isEqualToString:kUnverifiedEmailErrorMessage]) {
  1278. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeUnverifiedEmail
  1279. message:serverErrorMessage];
  1280. }
  1281. if ([shortErrorMessage isEqualToString:kSecondFactorExistsErrorMessage]) {
  1282. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeSecondFactorAlreadyEnrolled
  1283. message:serverErrorMessage];
  1284. }
  1285. if ([shortErrorMessage isEqualToString:kSecondFactorLimitExceededErrorMessage]) {
  1286. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeMaximumSecondFactorCountExceeded
  1287. message:serverErrorMessage];
  1288. }
  1289. if ([shortErrorMessage isEqualToString:kUnsupportedFirstFactorErrorMessage]) {
  1290. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeUnsupportedFirstFactor
  1291. message:serverErrorMessage];
  1292. }
  1293. if ([shortErrorMessage isEqualToString:kEmailChangeNeedsVerificationErrorMessage]) {
  1294. return [FIRAuthErrorUtils errorWithCode:FIRAuthInternalErrorCodeEmailChangeNeedsVerification
  1295. message:serverErrorMessage];
  1296. }
  1297. if ([shortErrorMessage isEqualToString:kTenantIDMismatch]) {
  1298. return [FIRAuthErrorUtils tenantIDMismatchError];
  1299. }
  1300. if ([shortErrorMessage isEqualToString:kUnsupportedTenantOperation]) {
  1301. return [FIRAuthErrorUtils unsupportedTenantOperationError];
  1302. }
  1303. if ([shortErrorMessage isEqualToString:kBlockingCloudFunctionErrorResponse]) {
  1304. return
  1305. [FIRAuthErrorUtils blockingCloudFunctionServerResponseWithMessage:serverDetailErrorMessage];
  1306. }
  1307. // In this case we handle an error that might be specified in the underlying errors dictionary,
  1308. // the error message in determined based on the @c reason key in the dictionary.
  1309. if (errorDictionary[kErrorsKey]) {
  1310. // Check for underlying error with reason = keyInvalid;
  1311. id underlyingErrors = errorDictionary[kErrorsKey];
  1312. if ([underlyingErrors isKindOfClass:[NSArray class]]) {
  1313. NSArray *underlyingErrorsArray = (NSArray *)underlyingErrors;
  1314. for (id underlyingError in underlyingErrorsArray) {
  1315. if ([underlyingError isKindOfClass:[NSDictionary class]]) {
  1316. NSDictionary *underlyingErrorDictionary = (NSDictionary *)underlyingError;
  1317. NSString *reason = underlyingErrorDictionary[kReasonKey];
  1318. if ([reason hasPrefix:kInvalidKeyReasonValue]) {
  1319. return [FIRAuthErrorUtils invalidAPIKeyError];
  1320. }
  1321. if ([reason isEqualToString:kAppNotAuthorizedReasonValue]) {
  1322. return [FIRAuthErrorUtils appNotAuthorizedError];
  1323. }
  1324. }
  1325. }
  1326. }
  1327. }
  1328. return nil;
  1329. }
  1330. @end
  1331. NS_ASSUME_NONNULL_END